commit bf9064a9c9d9606b9b3310075f8f3028057aac5b Author: Ashutosh Date: Thu Aug 1 13:46:46 2024 +0530 fist commit ftc staff app clone diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07baad1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# 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 + +pubspec.lock \ No newline at end of file diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..09a39aa --- /dev/null +++ b/.metadata @@ -0,0 +1,33 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "ead455963c12b453cdb2358cad34969c76daf180" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: ead455963c12b453cdb2358cad34969c76daf180 + base_revision: ead455963c12b453cdb2358cad34969c76daf180 + - platform: android + create_revision: ead455963c12b453cdb2358cad34969c76daf180 + base_revision: ead455963c12b453cdb2358cad34969c76daf180 + - platform: ios + create_revision: ead455963c12b453cdb2358cad34969c76daf180 + base_revision: ead455963c12b453cdb2358cad34969c76daf180 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/README.md b/README.md new file mode 100644 index 0000000..c898bfa --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# ftc_mobile_app +A Mobile App for Android and IOS +## Documentation +1- [FIGMA-VIEW-ONLY](https://www.figma.com/file/62LScCEOiGqin91Esm6ht7/FCT-V04?type=design&node-id=179-4255&mode=design&t=jPwwPCxekItUc7KH-0) + +## Tech Stack + +**Mobile:** Flutter + +## Flutter Version +- Flutter Version 3.13 + +**Available Shifts:** Available Shifts or open shifts are shifts having unassigned staff member or patient. + + +NSAppTransportSecurity + +NSAllowsArbitraryLoads + + + +Remove these lines from info.plist. These are added to view http:// links. Remove them for security \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..64a3dd4 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,67 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +android { + namespace "com.ftc.app.ftc_mobile_app" + compileSdkVersion flutter.compileSdkVersion + ndkVersion "25.1.8937393" + + 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.ftc.app.ftc_mobile_app" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdkVersion 21 + 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 {} diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..33a8bfb --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "583559514958", + "project_id": "ftc-services-ea8d6", + "storage_bucket": "ftc-services-ea8d6.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:583559514958:android:89af243ca4a3888a32ec1f", + "android_client_info": { + "package_name": "com.ftc.app.ftc_mobile_app" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyDF2vZbiWQROGvyvLeeCmAjPLnPLYjE6Os" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..740033b --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/ftc/app/ftc_mobile_app/MainActivity.kt b/android/app/src/main/kotlin/com/ftc/app/ftc_mobile_app/MainActivity.kt new file mode 100644 index 0000000..e341968 --- /dev/null +++ b/android/app/src/main/kotlin/com/ftc/app/ftc_mobile_app/MainActivity.kt @@ -0,0 +1,6 @@ +package com.ftc.app.ftc_mobile_app + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/android/app/src/main/res/drawable-v21/background.png b/android/app/src/main/res/drawable-v21/background.png new file mode 100644 index 0000000..62420b9 Binary files /dev/null and b/android/app/src/main/res/drawable-v21/background.png differ diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f88598c --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/app/src/main/res/drawable/background.png b/android/app/src/main/res/drawable/background.png new file mode 100644 index 0000000..62420b9 Binary files /dev/null and b/android/app/src/main/res/drawable/background.png differ diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..f88598c --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..fcc4b4a Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png new file mode 100644 index 0000000..fcc4b4a Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..9ed3a53 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/launcher_icon.png b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png new file mode 100644 index 0000000..9ed3a53 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..397b9b8 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png new file mode 100644 index 0000000..397b9b8 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..c9c8c79 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png new file mode 100644 index 0000000..c9c8c79 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..d3a9c2b Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png new file mode 100644 index 0000000..d3a9c2b Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/values-night-v31/styles.xml b/android/app/src/main/res/values-night-v31/styles.xml new file mode 100644 index 0000000..020eb64 --- /dev/null +++ b/android/app/src/main/res/values-night-v31/styles.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..dbc9ea9 --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/android/app/src/main/res/values-v31/styles.xml b/android/app/src/main/res/values-v31/styles.xml new file mode 100644 index 0000000..345c07c --- /dev/null +++ b/android/app/src/main/res/values-v31/styles.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..0d1fa8f --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..0391438 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,34 @@ +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.3.0' + // START: FlutterFire Configuration + classpath 'com.google.gms:google-services:4.3.10' + // END: FlutterFire Configuration + 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') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3c472b9 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..55c4ca8 --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,20 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + } + settings.ext.flutterSdkPath = flutterSdkPath() + + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + + plugins { + id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false + } +} + +include ":app" + +apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/assets/consent-capacity.html b/assets/consent-capacity.html new file mode 100644 index 0000000..8c96d45 --- /dev/null +++ b/assets/consent-capacity.html @@ -0,0 +1,631 @@ + + +
+
+

+ Is an MCA required to be completed? + +

+
+ +
+

Comments

+
+ +
+ +
+
+

+ This Mental Capacity Assessment must adhere to the Act’s 5 + principles: +

+
    +
  • Every adult has the right to make his or her own decisions and must be + assumed + to + have capacity to make them unless proved otherwise. +
  • +
  • A person must be given all practicable help before anyone treat them as not + being + able to make their own decisions. +
  • +
  • Just because an individual makes what may be seen as an unwise decision, + they + should + not be treated as lacking capacity to make that decision +
  • +
  • Anything done or any decision made on behalf of a person who lacks capacity + must + be + done in their best interests. +
  • +
  • Anything done or any decision made on behalf of a person who lacks capacity + should + be the least restrictive of their basic rights and freedoms. +
  • +
+
+
+

This form has been developed to support compliance with the Mental Capacity Act + 2005. + There is a statutory requirement for anyone undertaking an assessment to have + regard + to + the Code of Practice for the Mental Capacity Act. References given below refer + to + the + relevant paragraphs of the Mental Capacity Act Code of Practice. Please also + refer + to + MCA and DoLS Policy and Guidance. (For day to day decisions, please print out/ + fill + in + relevant sections 1.1 - 1.10) +

+

+ Detail + + + +

+
+
+ + +
+
+

+ 1.2 What is the specific decision relevant to this mental capacity + assessment? Please ensure that the decision is phrased in a way to + enable + all viable options to be discussed. The MCA Code paragraph 4.4 states 'An + assessment + of + a person’s capacity must be based on their ability to make a specific decision + at + the + time it needs to be made, and not their ability to make decisions in general.' +

+

+ Detail + + + +

+
+
+ + +
+
+

+ 1.3 Person undertaking/or who has undertaken this assessment of + capacity? The person with greatest responsibility for the specific + decision + is known as the ‘decision-maker’ and should assess capacity. The decision maker + is + the + person intending to make the decision or carry out the action. Complex decisions + may + require specialist assessment - seek guidance. See 4.38 to 4.43 of the Code.

+
+
+

+ Name + + + +

+
+
+

+ Role + + + +

+
+
+

+ Organisation + + + +

+
+
+

+ Address + + + +

+
+
+

+ Tel + + + +

+
+
+

+ Email + + + +

+
+
+

+ Date and time of assessment + + + +

+
+
+ + +
+
+

+ 1.4 What concerns/triggers have given rise to this assessment of + capacity? People have the right to make decisions that others might + think + are unwise. A person who makes a decision that others think is unwise should not + automatically be labelled as lacking the capacity to make a decision. See MCA + Code + 4.35. +

+
+
+

+ What is the reason to believe this person may lack capacity to make this + particular decision? State your evidence:

+

+ Detail + + + +

+
+
+ + +
+
+

+ 1.5 Record your evidence here of the actions you have taken to support + the + person. + Consider what kind of help and support you can give the person to help them + understand, + retain, weigh up information and communicate their decision.

+

+ Have you discussed with the person and/or appropriate others the most + suitable + venue for the assessment? + For example: Does the person feel more comfortable in their own room? Does it + need + to be + quiet? See MCA Code 3.13.

+

+ Have you discussed with the person and/or appropriate others to + establish + timing + of assessment For example: Is there a time of day that is better + for + the + person? Would it help to have a particular person present? See MCA Code 3.14. +

+

+ Does the person have any language/communication issues? For + example: + Do they have hearing or speech difficulties? Do you need an interpreter? Do they + communicate using special equipment e.g. a light talker communication device? + See + MCA + Code 3.11.

+

+ Have you provided all the information, regarding all viable and + available + options that the person needs to consider, to make an informed + decision? See + MCA Code 3.7. The assessor must ensure that the person has:

+
    +
  1. Sufficiently detailed alternative plans explained to them to allow them to + weigh + up + the alternatives and make an informed choice where possible. +
  2. +
  3. Been supported by the assessor to explore the reasonably foreseeable + consequences of + deciding one way or another, or failing to make the decision. +
  4. +
+
+

+ Describe + + + +

+ +

+ Viable options considered + + + +

+ +
+

+ If the decision is not urgent can it be delayed because the person is + likely + to + regain or develop the capacity to make it for themselves?

+
+
+
+ + +
+
+
+
+

+ Explain why you have ticked box(s) + +

+
+
+ + +
+
+

+ 1.6 Two Stage Capacity Assessment Answer the question with + facts. + The + questions cannot be answered with a simple “yes” or “no” and you are asked to + describe + the assessment process. See MCA Code Ch. 4.

+

Stage 1. Is there an impairment or disturbance in the functioning of the + person’s + mind or brain The person may not have a diagnosis but the Code says + that + proof + of an impairment or disturbance of the functioning of the mind or brain is + required. + You + should record here your reasons for believing this to be the case. See 4.11 - + 4.12 + of + the Code. This could be because of, for example, a head injury, a suspected + infection or + stroke, a diagnosed dementia, mental illness, or learning disability.

+
+

+ +

+
+ +

+ Describe + + + +

+
+
+

+ If the person does not meet Stage 1, the assessment should immediately + stop. + Stage 2. Record here how the identified impairment or disturbance in Stage 1 + is + affecting the person’s ability to make the decision. + See 4.13 to 4.30 of the Code.

+

Can the person understand the information relevant to the + decision? + See + 4.16 to 4.19 of the Code.

+
+

+ + +

+
+

+ Describe how you assessed this + + + +

+
+
+

+ Can they retain that information long enough to make the + decision? See + 4.20 to 4.22 of the Code.

+
+

+ + +

+
+

+ Describe how you assessed this + + + +

+ +
+
+

+ Can they use or weigh up that information as part of the process of + making + the + decision? See 4.21 to 4.22 of the Code.

+
+

+ +

+
+

+ Describe how you assessed this + + + +

+
+
+

+ Can they communicate their decision, by any means available to + them? See + 4.23 to 4.25 of the Code.

+
+

+ + +

+
+

+ Describe the reasons for your conclusion + + + +

+
+
+

+ NB. If all of the answers to the four questions above are YES, then + Stage 2 + is + not met and the assessment must end.

+

Stage 3: Causative Nexus There is a causative link between the + impairment or disturbance in the functioning of mind and brain AND the inability + to + make + the required decision. You must be able to evidence that the reason the person + is + unable + to make the decision is because of the impairment or disturbance in the + functioning + of + mind or brain and for no other reason.

+ +
+
+
+ + +
+
+
+
+

+ Evidence + +

+
+
+ + +
+
+

+ 1.7 Lack of mental capacity as a result of an impairment/disturbance in + mind/brain must be distinguished from a situation where a person is unable + to + make + their own decision as a result of duress or undue influence. A person who + has + the + mental capacity to make decisions may have their ability to give free and + true + consent impaired if they are under constraint, coercion or undue influence. + Duress + and undue influence may be affected by eroded confidence due to fear of + reprisal + or + abandonment, sense of obligation, cultural factors, power relationships or + coercive + control within domestic abuse. Do you have a concern that the person may be + under + duress/coercion or undue influence in relation to the making of this + decision? + If + so, this will not satisfy the Stage 1 (Diagnostic) test. You have to have an + impairment or disturbance of the mind or brain to satisfy that + test. +

+

Do you have a concern that the person may be under duress, coercion or + undue + influence?

+ +
+

+ +

+
+

+ If yes, what is your evidence for saying this? + + +

+

If yes, what actions you intend to take (including consideration of seeking + management/legal advice)

+

+ Describe + +

+
+
+ + +
+
+

+ 1.8 Please record here any further information or content of your + interview + with + the person.

+

+ Describe + +

+
+
+ + +
+
+

+ 1.9 Determination of Capacity

+

I have assessed this person’s capacity to make the specific decision and + determined + on + the balance of probability that they do not have the capacity to make this + decision + at + this time.

+
+
+

+ Name + +

+ +
+
+

+ Date + + + +

+
+
+

I have assessed this person’s capacity to make the specific decision and + determined + that + on the balance of probability that they have the capacity to make this decision + at + this + time.

+
+
+

+ Name + +

+
+
+

+ Date + + + +

+
+
+

+ Is an IMCA Required?

+
    +
  • If the person (16+) is unbefriended and the decision is about a change of + accommodation, or serious medical treatment, you MUST involve an IMCA. +
  • +
  • If a friend or family member exists, but they may not act in the person’s + best + interests (for example because they are the alleged victim or abuser in a + Safeguarding Adults investigation) you MAY involve an IMCA. +
  • +
  • If the person is unbefriended and a health or social care review is being + carried + out, you MAY CONSIDER involving an IMCA as good practice. +
  • +
  • Although you may involve an IMCA under the Mental Capacity Act legislation, + if + there + is no appropriate person, for people over age 18, you MUST instruct a Care + Act + Advocate if the person has substantial difficulty engaging with the relevant + assessment & support planning/review/safeguarding process. Please use the + most + appropriate legislation to ensure entitlement to advocacy. +
  • +
+ +

Does the individual require an IMCA?

+
+

+
+

+ If not, please give reasons. + +

+

+ Date + +

+
+
+

+ Assessors Details.

+
+

+ Name + +

+
+
+

+ Designation + +

+
+ +
+

+ Base / Address + +

+ +
+
+

+ Contact Details + +

+
+
+
+
+ + + diff --git a/assets/fonts/Roboto-Black.ttf b/assets/fonts/Roboto-Black.ttf new file mode 100644 index 0000000..0112e7d Binary files /dev/null and b/assets/fonts/Roboto-Black.ttf differ diff --git a/assets/fonts/Roboto-BlackItalic.ttf b/assets/fonts/Roboto-BlackItalic.ttf new file mode 100644 index 0000000..b2c6aca Binary files /dev/null and b/assets/fonts/Roboto-BlackItalic.ttf differ diff --git a/assets/fonts/Roboto-Bold.ttf b/assets/fonts/Roboto-Bold.ttf new file mode 100644 index 0000000..43da14d Binary files /dev/null and b/assets/fonts/Roboto-Bold.ttf differ diff --git a/assets/fonts/Roboto-BoldItalic.ttf b/assets/fonts/Roboto-BoldItalic.ttf new file mode 100644 index 0000000..bcfdab4 Binary files /dev/null and b/assets/fonts/Roboto-BoldItalic.ttf differ diff --git a/assets/fonts/Roboto-Italic.ttf b/assets/fonts/Roboto-Italic.ttf new file mode 100644 index 0000000..1b5eaa3 Binary files /dev/null and b/assets/fonts/Roboto-Italic.ttf differ diff --git a/assets/fonts/Roboto-Light.ttf b/assets/fonts/Roboto-Light.ttf new file mode 100644 index 0000000..e7307e7 Binary files /dev/null and b/assets/fonts/Roboto-Light.ttf differ diff --git a/assets/fonts/Roboto-LightItalic.ttf b/assets/fonts/Roboto-LightItalic.ttf new file mode 100644 index 0000000..2d277af Binary files /dev/null and b/assets/fonts/Roboto-LightItalic.ttf differ diff --git a/assets/fonts/Roboto-Medium.ttf b/assets/fonts/Roboto-Medium.ttf new file mode 100644 index 0000000..ac0f908 Binary files /dev/null and b/assets/fonts/Roboto-Medium.ttf differ diff --git a/assets/fonts/Roboto-MediumItalic.ttf b/assets/fonts/Roboto-MediumItalic.ttf new file mode 100644 index 0000000..fc36a47 Binary files /dev/null and b/assets/fonts/Roboto-MediumItalic.ttf differ diff --git a/assets/fonts/Roboto-Regular.ttf b/assets/fonts/Roboto-Regular.ttf new file mode 100644 index 0000000..ddf4bfa Binary files /dev/null and b/assets/fonts/Roboto-Regular.ttf differ diff --git a/assets/fonts/Roboto-Thin.ttf b/assets/fonts/Roboto-Thin.ttf new file mode 100644 index 0000000..2e0dee6 Binary files /dev/null and b/assets/fonts/Roboto-Thin.ttf differ diff --git a/assets/fonts/Roboto-ThinItalic.ttf b/assets/fonts/Roboto-ThinItalic.ttf new file mode 100644 index 0000000..084f9c0 Binary files /dev/null and b/assets/fonts/Roboto-ThinItalic.ttf differ diff --git a/assets/icon/icon.png b/assets/icon/icon.png new file mode 100644 index 0000000..0d1aa2e Binary files /dev/null and b/assets/icon/icon.png differ diff --git a/assets/images/png/app-logo-icon.png b/assets/images/png/app-logo-icon.png new file mode 100644 index 0000000..3c186b0 Binary files /dev/null and b/assets/images/png/app-logo-icon.png differ diff --git a/assets/images/png/app_icon.png b/assets/images/png/app_icon.png new file mode 100644 index 0000000..0d1aa2e Binary files /dev/null and b/assets/images/png/app_icon.png differ diff --git a/assets/images/png/human_body_front_back.png b/assets/images/png/human_body_front_back.png new file mode 100644 index 0000000..15b784b Binary files /dev/null and b/assets/images/png/human_body_front_back.png differ diff --git a/assets/images/png/ratings/ic_angry.webp b/assets/images/png/ratings/ic_angry.webp new file mode 100644 index 0000000..c5ccce4 Binary files /dev/null and b/assets/images/png/ratings/ic_angry.webp differ diff --git a/assets/images/png/ratings/ic_bored.webp b/assets/images/png/ratings/ic_bored.webp new file mode 100644 index 0000000..2d144c4 Binary files /dev/null and b/assets/images/png/ratings/ic_bored.webp differ diff --git a/assets/images/png/ratings/ic_calm.webp b/assets/images/png/ratings/ic_calm.webp new file mode 100644 index 0000000..848f3c1 Binary files /dev/null and b/assets/images/png/ratings/ic_calm.webp differ diff --git a/assets/images/png/ratings/ic_confident.webp b/assets/images/png/ratings/ic_confident.webp new file mode 100644 index 0000000..ca1a714 Binary files /dev/null and b/assets/images/png/ratings/ic_confident.webp differ diff --git a/assets/images/png/ratings/ic_excited.webp b/assets/images/png/ratings/ic_excited.webp new file mode 100644 index 0000000..044f52f Binary files /dev/null and b/assets/images/png/ratings/ic_excited.webp differ diff --git a/assets/images/png/ratings/ic_happy.webp b/assets/images/png/ratings/ic_happy.webp new file mode 100644 index 0000000..f4a039d Binary files /dev/null and b/assets/images/png/ratings/ic_happy.webp differ diff --git a/assets/images/png/ratings/ic_hopeful.webp b/assets/images/png/ratings/ic_hopeful.webp new file mode 100644 index 0000000..752cf42 Binary files /dev/null and b/assets/images/png/ratings/ic_hopeful.webp differ diff --git a/assets/images/png/ratings/ic_nervous.webp b/assets/images/png/ratings/ic_nervous.webp new file mode 100644 index 0000000..01ddf9b Binary files /dev/null and b/assets/images/png/ratings/ic_nervous.webp differ diff --git a/assets/images/png/ratings/ic_proud.webp b/assets/images/png/ratings/ic_proud.webp new file mode 100644 index 0000000..7ad859b Binary files /dev/null and b/assets/images/png/ratings/ic_proud.webp differ diff --git a/assets/images/png/ratings/ic_relaxed.webp b/assets/images/png/ratings/ic_relaxed.webp new file mode 100644 index 0000000..752cf42 Binary files /dev/null and b/assets/images/png/ratings/ic_relaxed.webp differ diff --git a/assets/images/png/ratings/ic_sad.webp b/assets/images/png/ratings/ic_sad.webp new file mode 100644 index 0000000..d5c4c35 Binary files /dev/null and b/assets/images/png/ratings/ic_sad.webp differ diff --git a/assets/images/png/ratings/ic_scared.webp b/assets/images/png/ratings/ic_scared.webp new file mode 100644 index 0000000..6816eda Binary files /dev/null and b/assets/images/png/ratings/ic_scared.webp differ diff --git a/assets/images/png/ratings/ic_tired.webp b/assets/images/png/ratings/ic_tired.webp new file mode 100644 index 0000000..cd47171 Binary files /dev/null and b/assets/images/png/ratings/ic_tired.webp differ diff --git a/assets/images/png/ratings/ic_worried.webp b/assets/images/png/ratings/ic_worried.webp new file mode 100644 index 0000000..c64ebf6 Binary files /dev/null and b/assets/images/png/ratings/ic_worried.webp differ diff --git a/assets/images/svg/arrow-next-icon.svg b/assets/images/svg/arrow-next-icon.svg new file mode 100644 index 0000000..df71b4e --- /dev/null +++ b/assets/images/svg/arrow-next-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/back_icon.svg b/assets/images/svg/back_icon.svg new file mode 100644 index 0000000..aaf9e07 --- /dev/null +++ b/assets/images/svg/back_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/bell_icon.svg b/assets/images/svg/bell_icon.svg new file mode 100644 index 0000000..e4f2ba8 --- /dev/null +++ b/assets/images/svg/bell_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/calendar_appointment_icon.svg b/assets/images/svg/calendar_appointment_icon.svg new file mode 100644 index 0000000..4ff7a61 --- /dev/null +++ b/assets/images/svg/calendar_appointment_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesCategories/ic_general.svg b/assets/images/svg/careNotesCategories/ic_general.svg new file mode 100644 index 0000000..93135b3 --- /dev/null +++ b/assets/images/svg/careNotesCategories/ic_general.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesCategories/ic_health.svg b/assets/images/svg/careNotesCategories/ic_health.svg new file mode 100644 index 0000000..2ce1a80 --- /dev/null +++ b/assets/images/svg/careNotesCategories/ic_health.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesCategories/ic_independent_living.svg b/assets/images/svg/careNotesCategories/ic_independent_living.svg new file mode 100644 index 0000000..60c6389 --- /dev/null +++ b/assets/images/svg/careNotesCategories/ic_independent_living.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/careNotesCategories/ic_intractions.svg b/assets/images/svg/careNotesCategories/ic_intractions.svg new file mode 100644 index 0000000..43ec814 --- /dev/null +++ b/assets/images/svg/careNotesCategories/ic_intractions.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesCategories/ic_mental_wellbeing.svg b/assets/images/svg/careNotesCategories/ic_mental_wellbeing.svg new file mode 100644 index 0000000..76fb16c --- /dev/null +++ b/assets/images/svg/careNotesCategories/ic_mental_wellbeing.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/careNotesCategories/ic_personal_care.svg b/assets/images/svg/careNotesCategories/ic_personal_care.svg new file mode 100644 index 0000000..148cb84 --- /dev/null +++ b/assets/images/svg/careNotesCategories/ic_personal_care.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_abc.svg b/assets/images/svg/careNotesSubcatgeories/ic_abc.svg new file mode 100644 index 0000000..9f23300 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_abc.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_activity.svg b/assets/images/svg/careNotesSubcatgeories/ic_activity.svg new file mode 100644 index 0000000..93f73f8 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_activity.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_appointment.svg b/assets/images/svg/careNotesSubcatgeories/ic_appointment.svg new file mode 100644 index 0000000..80c93a8 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_appointment.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_cleaning.svg b/assets/images/svg/careNotesSubcatgeories/ic_cleaning.svg new file mode 100644 index 0000000..5cb112f --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_cleaning.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_consent.svg b/assets/images/svg/careNotesSubcatgeories/ic_consent.svg new file mode 100644 index 0000000..09d2c97 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_consent.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_cooking.svg b/assets/images/svg/careNotesSubcatgeories/ic_cooking.svg new file mode 100644 index 0000000..9c74154 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_cooking.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_education.svg b/assets/images/svg/careNotesSubcatgeories/ic_education.svg new file mode 100644 index 0000000..7faafe2 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_education.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_email.svg b/assets/images/svg/careNotesSubcatgeories/ic_email.svg new file mode 100644 index 0000000..4420d74 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_email.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_finance.svg b/assets/images/svg/careNotesSubcatgeories/ic_finance.svg new file mode 100644 index 0000000..f40edd6 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_finance.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_hydration.svg b/assets/images/svg/careNotesSubcatgeories/ic_hydration.svg new file mode 100644 index 0000000..35814ff --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_hydration.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_injury.svg b/assets/images/svg/careNotesSubcatgeories/ic_injury.svg new file mode 100644 index 0000000..5722cbd --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_injury.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_laundry.svg b/assets/images/svg/careNotesSubcatgeories/ic_laundry.svg new file mode 100644 index 0000000..db74e4d --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_laundry.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_meeting.svg b/assets/images/svg/careNotesSubcatgeories/ic_meeting.svg new file mode 100644 index 0000000..d3956a7 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_meeting.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_mood.svg b/assets/images/svg/careNotesSubcatgeories/ic_mood.svg new file mode 100644 index 0000000..39a6121 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_mood.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_mouth_hygiene.svg b/assets/images/svg/careNotesSubcatgeories/ic_mouth_hygiene.svg new file mode 100644 index 0000000..3cd26ff --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_mouth_hygiene.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_note.svg b/assets/images/svg/careNotesSubcatgeories/ic_note.svg new file mode 100644 index 0000000..8133597 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_note.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_other_interactions.svg b/assets/images/svg/careNotesSubcatgeories/ic_other_interactions.svg new file mode 100644 index 0000000..eb4da65 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_other_interactions.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_others.svg b/assets/images/svg/careNotesSubcatgeories/ic_others.svg new file mode 100644 index 0000000..f28f5e2 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_others.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_physical_intervention.svg b/assets/images/svg/careNotesSubcatgeories/ic_physical_intervention.svg new file mode 100644 index 0000000..241b018 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_physical_intervention.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_public_interaction.svg b/assets/images/svg/careNotesSubcatgeories/ic_public_interaction.svg new file mode 100644 index 0000000..c1fa5a9 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_public_interaction.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_reviews.svg b/assets/images/svg/careNotesSubcatgeories/ic_reviews.svg new file mode 100644 index 0000000..55be539 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_reviews.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_safeguarding.svg b/assets/images/svg/careNotesSubcatgeories/ic_safeguarding.svg new file mode 100644 index 0000000..a342a8d --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_safeguarding.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_search.svg b/assets/images/svg/careNotesSubcatgeories/ic_search.svg new file mode 100644 index 0000000..999539b --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_search.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_shower.svg b/assets/images/svg/careNotesSubcatgeories/ic_shower.svg new file mode 100644 index 0000000..21feafc --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_shower.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_sleep.svg b/assets/images/svg/careNotesSubcatgeories/ic_sleep.svg new file mode 100644 index 0000000..3455861 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_sleep.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_telephone.svg b/assets/images/svg/careNotesSubcatgeories/ic_telephone.svg new file mode 100644 index 0000000..8aab804 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_telephone.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_toileting.svg b/assets/images/svg/careNotesSubcatgeories/ic_toileting.svg new file mode 100644 index 0000000..fc5df26 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_toileting.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/careNotesSubcatgeories/ic_weight_height.svg b/assets/images/svg/careNotesSubcatgeories/ic_weight_height.svg new file mode 100644 index 0000000..fa1c4d0 --- /dev/null +++ b/assets/images/svg/careNotesSubcatgeories/ic_weight_height.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/images/svg/care_plan_icon.svg b/assets/images/svg/care_plan_icon.svg new file mode 100644 index 0000000..20d6551 --- /dev/null +++ b/assets/images/svg/care_plan_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/clock_icon.svg b/assets/images/svg/clock_icon.svg new file mode 100644 index 0000000..723983c --- /dev/null +++ b/assets/images/svg/clock_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/dustbin_red.svg b/assets/images/svg/dustbin_red.svg new file mode 100644 index 0000000..c02ae7f --- /dev/null +++ b/assets/images/svg/dustbin_red.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/eclipse.svg b/assets/images/svg/eclipse.svg new file mode 100644 index 0000000..0127831 --- /dev/null +++ b/assets/images/svg/eclipse.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/flag_icon.svg b/assets/images/svg/flag_icon.svg new file mode 100644 index 0000000..9069431 --- /dev/null +++ b/assets/images/svg/flag_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/folder_icon.svg b/assets/images/svg/folder_icon.svg new file mode 100644 index 0000000..543eacf --- /dev/null +++ b/assets/images/svg/folder_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/got_to_arrow_button.svg b/assets/images/svg/got_to_arrow_button.svg new file mode 100644 index 0000000..0893f13 --- /dev/null +++ b/assets/images/svg/got_to_arrow_button.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/human_body_front_back.svg b/assets/images/svg/human_body_front_back.svg new file mode 100644 index 0000000..bc2c1b2 --- /dev/null +++ b/assets/images/svg/human_body_front_back.svg @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/svg/ic_add.svg b/assets/images/svg/ic_add.svg new file mode 100644 index 0000000..898a767 --- /dev/null +++ b/assets/images/svg/ic_add.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/ic_at.svg b/assets/images/svg/ic_at.svg new file mode 100644 index 0000000..4cf2b69 --- /dev/null +++ b/assets/images/svg/ic_at.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/lock-icon.svg b/assets/images/svg/lock-icon.svg new file mode 100644 index 0000000..74677bb --- /dev/null +++ b/assets/images/svg/lock-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/man_body_image.svg b/assets/images/svg/man_body_image.svg new file mode 100644 index 0000000..2412457 --- /dev/null +++ b/assets/images/svg/man_body_image.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/svg/man_image.svg b/assets/images/svg/man_image.svg new file mode 100644 index 0000000..8b9df03 --- /dev/null +++ b/assets/images/svg/man_image.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/images/svg/man_image_png.png b/assets/images/svg/man_image_png.png new file mode 100644 index 0000000..4a16f19 Binary files /dev/null and b/assets/images/svg/man_image_png.png differ diff --git a/assets/images/svg/menu_drawer_icon.svg b/assets/images/svg/menu_drawer_icon.svg new file mode 100644 index 0000000..0b55490 --- /dev/null +++ b/assets/images/svg/menu_drawer_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/menudrawerIcon.svg b/assets/images/svg/menudrawerIcon.svg new file mode 100644 index 0000000..0b55490 --- /dev/null +++ b/assets/images/svg/menudrawerIcon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/microphone_mike.svg b/assets/images/svg/microphone_mike.svg new file mode 100644 index 0000000..bf74cec --- /dev/null +++ b/assets/images/svg/microphone_mike.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/svg/nav_bar_calendar_icon.svg b/assets/images/svg/nav_bar_calendar_icon.svg new file mode 100644 index 0000000..fb6613c --- /dev/null +++ b/assets/images/svg/nav_bar_calendar_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/nav_bar_home_icon.svg b/assets/images/svg/nav_bar_home_icon.svg new file mode 100644 index 0000000..030587b --- /dev/null +++ b/assets/images/svg/nav_bar_home_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/nav_bar_message_icon.svg b/assets/images/svg/nav_bar_message_icon.svg new file mode 100644 index 0000000..38fc18c --- /dev/null +++ b/assets/images/svg/nav_bar_message_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/nav_bar_people_icon.svg b/assets/images/svg/nav_bar_people_icon.svg new file mode 100644 index 0000000..dbe0418 --- /dev/null +++ b/assets/images/svg/nav_bar_people_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/nav_bar_person_main_icon.svg b/assets/images/svg/nav_bar_person_main_icon.svg new file mode 100644 index 0000000..a5ce1b8 --- /dev/null +++ b/assets/images/svg/nav_bar_person_main_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/notes_icon.svg b/assets/images/svg/notes_icon.svg new file mode 100644 index 0000000..a3376e3 --- /dev/null +++ b/assets/images/svg/notes_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/pencil-icon.svg b/assets/images/svg/pencil-icon.svg new file mode 100644 index 0000000..d69ce45 --- /dev/null +++ b/assets/images/svg/pencil-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/pencil_outline_icon.svg b/assets/images/svg/pencil_outline_icon.svg new file mode 100644 index 0000000..82930f0 --- /dev/null +++ b/assets/images/svg/pencil_outline_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/people_unselected.svg b/assets/images/svg/people_unselected.svg new file mode 100644 index 0000000..56b73bc --- /dev/null +++ b/assets/images/svg/people_unselected.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/photo_gallery_icon.svg b/assets/images/svg/photo_gallery_icon.svg new file mode 100644 index 0000000..e6aca9f --- /dev/null +++ b/assets/images/svg/photo_gallery_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/plus_icon.svg b/assets/images/svg/plus_icon.svg new file mode 100644 index 0000000..2b3d4f1 --- /dev/null +++ b/assets/images/svg/plus_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/policies_icon.svg b/assets/images/svg/policies_icon.svg new file mode 100644 index 0000000..7f9532b --- /dev/null +++ b/assets/images/svg/policies_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/selected_calendar.svg b/assets/images/svg/selected_calendar.svg new file mode 100644 index 0000000..bb9c608 --- /dev/null +++ b/assets/images/svg/selected_calendar.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/setting_icon.svg b/assets/images/svg/setting_icon.svg new file mode 100644 index 0000000..732ddf5 --- /dev/null +++ b/assets/images/svg/setting_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/triangle.svg b/assets/images/svg/triangle.svg new file mode 100644 index 0000000..93337ae --- /dev/null +++ b/assets/images/svg/triangle.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/upload_icon.svg b/assets/images/svg/upload_icon.svg new file mode 100644 index 0000000..b894671 --- /dev/null +++ b/assets/images/svg/upload_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/upward_triangle_white.svg b/assets/images/svg/upward_triangle_white.svg new file mode 100644 index 0000000..93337ae --- /dev/null +++ b/assets/images/svg/upward_triangle_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/flutter_launcher_icons.yaml b/flutter_launcher_icons.yaml new file mode 100644 index 0000000..dc1b068 --- /dev/null +++ b/flutter_launcher_icons.yaml @@ -0,0 +1,6 @@ +flutter_launcher_icons: + ios: true + android: true + remove_alpha_ios: true + image_path: assets/images/png/app_icon.png + min_sdk_android: 21 # android min sdk min:16, default 21 diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..993c497 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,36 @@ +**/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 + +Podfile.lock diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..7c56964 --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +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 new file mode 100644 index 0000000..3648d51 --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1,3 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..293fdc4 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,81 @@ +# Uncomment this line to define a global platform for your project +platform :ios, '13.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__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + # fix xcode 15 DT_TOOLCHAIN_DIR - remove after fix oficially - https://github.com/CocoaPods/CocoaPods/issues/12065 + installer.aggregate_targets.each do |target| + target.xcconfigs.each do |variant, xcconfig| + xcconfig_path = target.client_root + target.xcconfig_relative_path(variant) + IO.write(xcconfig_path, IO.read(xcconfig_path).gsub("DT_TOOLCHAIN_DIR", "TOOLCHAIN_DIR")) + end + end + + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + # Start of the permission_handler configuration + target.build_configurations.each do |config| + if config.base_configuration_reference.is_a? Xcodeproj::Project::Object::PBXFileReference + xcconfig_path = config.base_configuration_reference.real_path + IO.write(xcconfig_path, IO.read(xcconfig_path).gsub("DT_TOOLCHAIN_DIR", "TOOLCHAIN_DIR")) + end + # https://developer.apple.com/forums/thread/725300 + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' + # You can enable the permissions needed here. For example to enable camera + # permission, just remove the `#` character in front so it looks like this: + # + # ## dart: PermissionGroup.camera + # 'PERMISSION_CAMERA=1' + # + # Preprocessor definitions can be found in: https://github.com/Baseflow/flutter-permission-handler/blob/master/permission_handler_apple/ios/Classes/PermissionHandlerEnums.h + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ + '$(inherited)', + + ## dart: PermissionGroup.camera + 'PERMISSION_CAMERA=1', + + ## dart: PermissionGroup.photos + 'PERMISSION_PHOTOS=1', + + ## dart: PermissionGroup.notification + 'PERMISSION_NOTIFICATIONS=1', + ] + end + # End of the permission_handler configuration + end +end diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..4d15d25 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,790 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 63520BA4F71FB4D8A253E588 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E3EE4C8588930D0F8F2A1412 /* Pods_Runner.framework */; }; + 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 */; }; + BF47A67D6C63BD427D9944E9 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2BD1715F0D2EA929B0F99F70 /* GoogleService-Info.plist */; }; + C3C4040E5FC6BC269C0D6024 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8445D989C9B52337F6BA022 /* Pods_RunnerTests.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 1DB7A3BC1E34CE1D92BE56DC /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 269C7CFC9628089F21B47590 /* 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 = ""; }; + 2BD1715F0D2EA929B0F99F70 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 459EA0914E67519EBF4B25E6 /* 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 = ""; }; + 562A95AA1491F6355A148CF1 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.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 = ""; }; + 7B57E706E6E3B8CEA8AEA027 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 81280B1ABFF29D2BFED465F1 /* 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; 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 = ""; }; + 98DDE2FF2C2AAACD00CABB40 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + D8445D989C9B52337F6BA022 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E3EE4C8588930D0F8F2A1412 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6E2C1DA63DD309A920C101B2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C3C4040E5FC6BC269C0D6024 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 63520BA4F71FB4D8A253E588 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + DB865E687BC430AF2C927C96 /* Pods */, + C2B1E9E776AB7DBF1AEA9E09 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 2BD1715F0D2EA929B0F99F70 /* GoogleService-Info.plist */, + 98DDE2FF2C2AAACD00CABB40 /* Runner.entitlements */, + 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 = ""; + }; + C2B1E9E776AB7DBF1AEA9E09 /* Frameworks */ = { + isa = PBXGroup; + children = ( + E3EE4C8588930D0F8F2A1412 /* Pods_Runner.framework */, + D8445D989C9B52337F6BA022 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + DB865E687BC430AF2C927C96 /* Pods */ = { + isa = PBXGroup; + children = ( + 459EA0914E67519EBF4B25E6 /* Pods-Runner.debug.xcconfig */, + 81280B1ABFF29D2BFED465F1 /* Pods-Runner.release.xcconfig */, + 269C7CFC9628089F21B47590 /* Pods-Runner.profile.xcconfig */, + 7B57E706E6E3B8CEA8AEA027 /* Pods-RunnerTests.debug.xcconfig */, + 1DB7A3BC1E34CE1D92BE56DC /* Pods-RunnerTests.release.xcconfig */, + 562A95AA1491F6355A148CF1 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 6A1F8EEDB083D69EAB7B6CA0 /* [CP] Check Pods Manifest.lock */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + 6E2C1DA63DD309A920C101B2 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + CA9ADD9C5C86681A0A15E5F9 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + CD9C997104B0ED0A353D7571 /* [CP] Embed Pods Frameworks */, + E5BBEA59BDE07864E2039D22 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + BF47A67D6C63BD427D9944E9 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 6A1F8EEDB083D69EAB7B6CA0 /* [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-RunnerTests-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 = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + CA9ADD9C5C86681A0A15E5F9 /* [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; + }; + CD9C997104B0ED0A353D7571 /* [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; + }; + E5BBEA59BDE07864E2039D22 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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 = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 11; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = S8LDT32AXN; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "FTC Services"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.business"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.ftcs.app; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = FTCS_Dev; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7B57E706E6E3B8CEA8AEA027 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.ftc.app.ftcMobileApp.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1DB7A3BC1E34CE1D92BE56DC /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.ftc.app.ftcMobileApp.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 562A95AA1491F6355A148CF1 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.ftc.app.ftcMobileApp.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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 = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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 = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 11; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = S8LDT32AXN; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "FTC Services"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.business"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.ftcs.app; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = FTCS_Dev; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_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,2"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 11; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = S8LDT32AXN; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "FTC Services"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.business"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.ftcs.app; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = FTCS_Dev; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..8e3ca5d --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/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/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..75e000f Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..5bf5570 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..e31e117 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..5fc546a Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..d86db7c Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..2a766b2 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..0bf6cdf Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..e31e117 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..0c82efc Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..9ef2186 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png new file mode 100644 index 0000000..7f2e8fd Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png new file mode 100644 index 0000000..bb90314 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png new file mode 100644 index 0000000..a660455 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png new file mode 100644 index 0000000..1255c37 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..9ef2186 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..c7c44ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png new file mode 100644 index 0000000..fcc4b4a Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png new file mode 100644 index 0000000..c9c8c79 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..d5982dd Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..ee05545 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..f9c1109 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json new file mode 100644 index 0000000..9f447e1 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "background.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png new file mode 100644 index 0000000..62420b9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..00cabce --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "LaunchImage.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "LaunchImage@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "LaunchImage@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..71e9c81 Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..71e9c81 Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..71e9c81 Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..55c0cae --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist new file mode 100644 index 0000000..81f05e6 --- /dev/null +++ b/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,30 @@ + + + + + API_KEY + AIzaSyAkJyvTX8oQSY9Ju3L39oupZbdy4Eo-RbA + GCM_SENDER_ID + 583559514958 + PLIST_VERSION + 1 + BUNDLE_ID + com.ftc.app.ftcMobileApp + PROJECT_ID + ftc-services-ea8d6 + STORAGE_BUCKET + ftc-services-ea8d6.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:583559514958:ios:5ec9cd88ca24777932ec1f + + \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..d432e37 --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,62 @@ + + + + + NSPhotoLibraryUsageDescription + This app needs access to the photo library to select photos and videos. + NSCameraUsageDescription + This app needs access to the camera to take photos and videos. + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Ftc Mobile App + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + FTC Services + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + ITSAppUsesNonExemptEncryption + + LSRequiresIPhoneOS + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements new file mode 100644 index 0000000..903def2 --- /dev/null +++ b/ios/Runner/Runner.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + development + + diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/ios/firebase_app_id_file.json b/ios/firebase_app_id_file.json new file mode 100644 index 0000000..ee45840 --- /dev/null +++ b/ios/firebase_app_id_file.json @@ -0,0 +1,7 @@ +{ + "file_generated_by": "FlutterFire CLI", + "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", + "GOOGLE_APP_ID": "1:583559514958:ios:5ec9cd88ca24777932ec1f", + "FIREBASE_PROJECT_ID": "ftc-services-ea8d6", + "GCM_SENDER_ID": "583559514958" +} \ No newline at end of file diff --git a/lib/controllers/auth_module/agency_sign_in_controller.dart b/lib/controllers/auth_module/agency_sign_in_controller.dart new file mode 100644 index 0000000..2f64a53 --- /dev/null +++ b/lib/controllers/auth_module/agency_sign_in_controller.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; + +class AgencySignInController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + final TextEditingController emailPhoneController = TextEditingController(); + RxString emailPhoneErrorMsg = "".obs; + RxBool isLoading = false.obs; + + bool validateEmailPhone() { + if(emailPhoneController.text.isEmpty) { + emailPhoneErrorMsg.value = ConstantText.kEmailPhoneIsRequired; + } else { + emailPhoneErrorMsg.value = ""; + } + return emailPhoneErrorMsg.isEmpty; + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + void onSendCodeButton() async { + if(validateEmailPhone()){ + isLoading.value = true; + var response = await AuthService().passwordLessLogin( + email: emailPhoneController.text, + ); + if (response is bool) { + await LocalStorageManager.saveSession( + tokenKey: LocalStorageKeys.kSaveEmailKey, + tokenValue: emailPhoneController.text, + ); + isLoading.value = false; + await Navigator.pushNamed( + screenKey.currentContext!, + CustomRouteNames.kOTPScreenRoute, + arguments: emailPhoneController.text, + ); + } else { + isLoading.value = false; + FrequentFunctions.showToast( + message: response["message"], + ); + } + } + } + + + @override + void dispose() { + emailPhoneController.dispose(); + Get.delete(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/controllers/auth_module/export_auth_module.dart b/lib/controllers/auth_module/export_auth_module.dart new file mode 100644 index 0000000..62fe35c --- /dev/null +++ b/lib/controllers/auth_module/export_auth_module.dart @@ -0,0 +1,4 @@ +export 'splash_screen_controller.dart'; +export 'sing_in_screen_controller.dart'; +export 'agency_sign_in_controller.dart'; +export 'otp_screen_controller.dart'; \ No newline at end of file diff --git a/lib/controllers/auth_module/otp_screen_controller.dart b/lib/controllers/auth_module/otp_screen_controller.dart new file mode 100644 index 0000000..3b9df7c --- /dev/null +++ b/lib/controllers/auth_module/otp_screen_controller.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; + +class OTPScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + // final TextEditingController otpController = TextEditingController(text: ""); + final TextEditingController otpController = TextEditingController(text: "123456"); + RxString otpErrorMsg = "".obs; + RxBool isLoading = false.obs; + + bool validateOTP() { + if(otpController.text.isEmpty) { + otpErrorMsg.value = ConstantText.kPleaseInputOTP; + } else if(otpController.text.length<6) { + otpErrorMsg.value = ConstantText.kInvalidOTP; + } else { + otpErrorMsg.value = ""; + } + return otpErrorMsg.isEmpty; + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + void onSubmitButton() async { + if (validateOTP()) { + var response = await AuthService().verifyOtp( + verificationCode: otpController.text, + ).showLoader(); + if (response is bool && response == true) { + Navigator.pushNamedAndRemoveUntil( + screenKey.currentContext!, + CustomRouteNames.kDashboardScreenRoute, + (route) => false, + ); + } else if (response is String) { + FrequentFunctions.showToast(message: response); + } else { + FrequentFunctions.showToast(message: response.toString()); + } + } + } + + @override + void dispose() { + otpController.dispose(); + Get.delete(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/controllers/auth_module/sing_in_screen_controller.dart b/lib/controllers/auth_module/sing_in_screen_controller.dart new file mode 100644 index 0000000..69b34d5 --- /dev/null +++ b/lib/controllers/auth_module/sing_in_screen_controller.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; + +class SignInScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + // final emailController = TextEditingController(text: ""); + // final passwordController = TextEditingController(text: ""); + final emailController = TextEditingController(text: "ashu@gmail.com"); + final passwordController = TextEditingController(text: "Abc@1234"); + final emailErrorMsg = "".obs, passwordErrorMsg = "".obs; + // final isRememberMe = false.obs; + final isLoading = false.obs; + + @override + void onInit() { + emailController.text = + LocalStorageManager.getSessionToken(tokenKey: LocalStorageKeys.kSaveEmailKey); + // isRememberMe.value = _sessionManagement + // .getSessionToken(tokenKey: SessionKeys.kRememberMeKey) + // .toLowerCase() == + // "true"; + emailController.text = 'ashu@gmail.com'; + super.onInit(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + bool validateEmail() { + if (emailController.text.isEmpty) { + emailErrorMsg.value = ConstantText.kEmailIsRequired; + } else if (!GetUtils.isEmail(emailController.text)) { + emailErrorMsg.value = ConstantText.kInvalidEmail; + } else { + emailErrorMsg.value = ""; + } + return emailErrorMsg.isEmpty; + } + + bool validatePassword() { + if (passwordController.text.isEmpty) { + passwordErrorMsg.value = ConstantText.kPasswordIsRequired; + } else { + passwordErrorMsg.value = ""; + } + return passwordErrorMsg.isEmpty; + } + + void onLogInButton() async { + if (validateEmail() & validatePassword()) { + isLoading.value = true; + var response = await AuthService().loginUser( + email: emailController.text, + password: passwordController.text, + ); + if (response is bool) { + // if (isRememberMe.isTrue) { + // await _sessionManagement.saveSession( + // tokenKey: SessionKeys.kRememberMeKey, + // tokenValue: "${isRememberMe.isTrue}", + // ); + // } + await LocalStorageManager.saveSession( + tokenKey: LocalStorageKeys.kSaveEmailKey, + tokenValue: emailController.text, + ); + isLoading.value = false; + await Navigator.pushNamed( + screenKey.currentContext!, + CustomRouteNames.kOTPScreenRoute, + ); + } else { + isLoading.value = false; + FrequentFunctions.showToast( + message: response["message"], + ); + } + } + } + + void onForgotButton() { + showDialog( + context: screenKey.currentState!.context, + builder: (BuildContext context) { + return CustomForgetPasswordDialog( + dialogButtonCloseText: "Cancel", + dialogButtonAcceptText: "Email Link", + dialogMessageText: + "A password reset link will be sent to your registered email.", + dialogMessageTextBold: "", + headingText: "Send Reset Password Link", + // acceptFunction: (String email) async { + // // await AuthService().forgetPassword(email: email); + // }, + ); + }, + ); + } + + void onAgencyLogInButton() async { + await Navigator.pushNamed( + screenKey.currentContext!, + CustomRouteNames.kAgencySignInScreenRoute, + ); + } + + @override + void dispose() { + emailController.dispose(); + passwordController.dispose(); + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/auth_module/splash_screen_controller.dart b/lib/controllers/auth_module/splash_screen_controller.dart new file mode 100644 index 0000000..1a6e6af --- /dev/null +++ b/lib/controllers/auth_module/splash_screen_controller.dart @@ -0,0 +1,119 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/utilities/app_session_manager.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class SplashScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + @override + void onInit() { + 5.seconds.delay(() { + // _gotoLoginScreen(); + + if (_loginTokenNotFound) { + _gotoLoginScreen(); + } else { + _checkOngoingShift(); + } + + }); + super.onInit(); + } + + //true if login token found in local storage + bool get _loginTokenNotFound { + return LocalStorageManager.getLoginToken().isEmpty; + } + + DateTime _getDateTimeObjFor({required int hour, required int minute}) { + return DateTime(DateTime.now().year, DateTime.now().month, + DateTime.now().day, hour, minute); + } + + //Todo: If Shift present then logout half an hour before start time + // Else logout 7am uk time + _checkOngoingShift() { + final shift = LocalStorageManager.getOngoingShift(); + + //if shift present + if (shift != null) { + ///boolean value [isShiftExpired] to check + ///if there is an expired shift in local storage. + final isShiftExpired = shift.endTime!.isBefore(TimeOfDay.now()); + if (isShiftExpired) { + LocalStorageManager.removeOngoingShift(); + } else { + final shiftNotStartedYet = shift.startTime!.isAfter(TimeOfDay.now()); + + if (shiftNotStartedYet) { + ///checking if shift is going to start in 30 minutes or less + if ((shift.startTime!.minute - TimeOfDay.now().minute) <= 30) { + _gotoLoginScreen(); + return; + } + + ///checking if shift is going to start in 60 minutes or less, then + ///start session timer which will alert session expire to user + ///if 30minutes left in shift start and logout the user. + if ((shift.startTime!.minute - TimeOfDay.now().minute) <= 60) { + //starting session timer + + final sessionExpireDateTime = _getDateTimeObjFor( + hour: shift.startTime!.hour, + minute: shift.startTime!.minute, + ).subtract(30.minutes); + + final millisLeft = sessionExpireDateTime.millisecondsSinceEpoch - + DateTime.now().millisecondsSinceEpoch; + + AppSessionManager.instance.startSessionTimer(millisLeft); + } + } + } + } else { + final currentTime = TimeOfDay.now(); + const time7am = TimeOfDay(hour: 7, minute: 0); + + ///checking if current time is before [7:00 AM] + if (currentTime.isBefore(time7am)) { + ///checking if remaining 30 minutes or less time + if ((time7am.minute - currentTime.minute) <= 30) { + //starting session timer + final dateTime7am = + _getDateTimeObjFor(hour: time7am.hour, minute: time7am.minute); + + final millisLeft = dateTime7am.millisecondsSinceEpoch - + DateTime.now().millisecondsSinceEpoch; + + AppSessionManager.instance.startSessionTimer(millisLeft); + } + } + } + + _gotoDashboardScreen(); + } + + _gotoLoginScreen() { + Navigator.pushNamedAndRemoveUntil( + screenKey.currentContext!, + CustomRouteNames.kLoginScreenRoute, + (route) => false, + ); + } + + _gotoDashboardScreen() { + Navigator.pushNamedAndRemoveUntil( + screenKey.currentContext!, + CustomRouteNames.kDashboardScreenRoute, + (route) => false, + ); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/clients/add_details_to_new_body_point_screen_controller.dart b/lib/controllers/clients/add_details_to_new_body_point_screen_controller.dart new file mode 100644 index 0000000..57182c6 --- /dev/null +++ b/lib/controllers/clients/add_details_to_new_body_point_screen_controller.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/HealthIssuesDetailsModel.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import 'careNoteFormControllers/category_subcategory_widget_controller.dart'; + +class AddDetailsToNewBodyPointScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final healthNoteController = TextEditingController(); + final complaintController = TextEditingController(); + + final catSubCatController = Get.put(CategorySubcategoryWidgetController()); + + ///[isEditing] will be true if using [AddDetailsToNewBodyPointScreen] screen to edit issue details, + /// or say issueData is not null + final isEditing = false.obs; + + String serviceUserId = ""; + HealthIssueDetailsModel? issueData; + + @override + void onReady() { + if (issueData != null) { + isEditing.value = true; + healthNoteController.text = issueData!.healthNote; + complaintController.text = issueData!.complaint; + } + catSubCatController.getBodyParts(); + super.onReady(); + } + + @override + void dispose() { + healthNoteController.dispose(); + complaintController.dispose(); + catSubCatController.dispose(); + super.dispose(); + } + + Future submitButtonPressed(BuildContext context) async { + if (isEditing.isFalse) { + if (catSubCatController.selectedBodyPart() == null) { + FrequentFunctions.showToast( + message: "Please select category first", + ); + return; + } + + if (catSubCatController.selectedBodyPart()!.subCategory.isNotEmpty && + catSubCatController.selectedSubcategory() == null) { + FrequentFunctions.showToast( + message: "Please select subcategory", + ); + return; + } + } + + if (healthNoteController.text.trim().isEmpty) { + FrequentFunctions.showToast(message: "Health Note is required"); + return; + } + if (complaintController.text.trim().isEmpty) { + FrequentFunctions.showToast(message: "Complaint is required"); + return; + } + + var result = (isEditing.isFalse) + ? await ClientService() + .addHealthIssue( + userId: serviceUserId, + category: catSubCatController.selectedSubcategory()?.id ?? + catSubCatController.selectedBodyPart()!.id, + healthNote: healthNoteController.text.trim(), + complaint: complaintController.text.trim()) + .showLoader() + : await ClientService() + .updateHealthIssueData( + issueId: issueData!.id, + categoryId: issueData!.bodyPointsCategory!.id, + healthNote: healthNoteController.text.trim(), + complaint: complaintController.text.trim()) + .showLoader(); + + if (result is! String) { + Navigator.of(context).pop(true); + } else { + if (result.isNotEmpty) { + FrequentFunctions.showToast(message: result); + } + } + + return; + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } +} diff --git a/lib/controllers/clients/add_new_document_screen_controller.dart b/lib/controllers/clients/add_new_document_screen_controller.dart new file mode 100644 index 0000000..d37e094 --- /dev/null +++ b/lib/controllers/clients/add_new_document_screen_controller.dart @@ -0,0 +1,127 @@ +import 'dart:io'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import '../../models/clients/documents_list_model.dart'; + +class AddNewDocumentScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + TextEditingController documentTitleController = TextEditingController(); + TextEditingController documentDetailsController = TextEditingController(); + dynamic arguments; + final docIdReceived = false.obs; + final viewOnly = false.obs; + final serviceUser = Rx(null); + DocumentModel documentModel = DocumentModel.empty(); + final docFile = Rx(null); + RxString docFilePath = "".obs; + + @override + void onInit() { + arguments = CustomRouteGenerator.argument; + if (arguments is List && + arguments[0] is DocumentModel && + arguments[1] is UserData) { + //Open for Editing + documentModel = arguments[0]; + docIdReceived.value = true; + docFilePath.value = documentModel.docPath; + documentTitleController.text = documentModel.title; + documentDetailsController.text = documentModel.details; + serviceUser.value = arguments[1]; + } else if (arguments is List && arguments[1] is bool) { + documentModel = arguments[0]; + docFilePath.value = documentModel.docPath; + viewOnly.value = true; + documentTitleController.text = documentModel.title; + documentDetailsController.text = documentModel.details; + } else if (arguments is UserData) { + //Open to add new document + serviceUser.value = arguments; + } + super.onInit(); + } + + onFileChooseButtonTap() async { + FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ["pdf", "doc", "docx", "xlsx", "xls"]); + + if (result != null) { + docFile.value = File(result.files.single.path!); + docFilePath.value = result.files.single.path!; + } + } + + Future submitButtonPressed() async { + if (validateFields()) { + if (docIdReceived.isTrue) { + //update doc code here + + String documentPath = docFilePath.value; + + //If doc file is picked than uploading file to server and will be used to update document + if (docFile() != null) { + final resp = await ClientService() + .uploadDocumentService(docPath: docFile()!.path) + .showLoader(); + if (resp is String) { + documentPath = resp; + } + } + + final response = await ClientService() + .updateDocumentService( + userId: serviceUser()!.id!, + docId: documentModel.id, + docPath: documentPath, + docDetails: documentDetailsController.text, + title: documentTitleController.text, + addedBy: LocalStorageManager.userId) + .showLoader(); + if (response is DocumentModel) { + backButtonPressed(argument: response); + } else { + FrequentFunctions.showToast(message: response); + } + } else if (viewOnly.isTrue) { + //view doc code here + } else { + // add doc code here + var response = await ClientService() + .addDocumentService( + userId: serviceUser()!.id!, + docPath: docFile.value!.path ?? "", + docDetails: documentDetailsController.text, + title: documentTitleController.text, + addedBy: LocalStorageManager.userId) + .showLoader(); + + backButtonPressed(argument: response); + } + } else if (viewOnly.isTrue) { + backButtonPressed(); + } else { + FrequentFunctions.showToast( + message: "Please fill all fields and add Required Document"); + } + } + + void backButtonPressed({dynamic argument}) { + // Get.delete(); + Navigator.of(screenKey.currentContext!).pop(argument); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + bool validateFields() { + return documentTitleController.text.isNotEmpty && + documentDetailsController.text.isNotEmpty && + (docFile.value != null || docIdReceived.isTrue); + } +} diff --git a/lib/controllers/clients/add_new_recent_incident_screen_controller.dart b/lib/controllers/clients/add_new_recent_incident_screen_controller.dart new file mode 100644 index 0000000..9d93e5c --- /dev/null +++ b/lib/controllers/clients/add_new_recent_incident_screen_controller.dart @@ -0,0 +1,166 @@ +import 'package:adoptive_calendar/adoptive_calendar.dart'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/view/screens/clients/add_new_recent_incident_screen.dart'; +import 'package:get/get.dart'; +import 'package:quill_html_editor/quill_html_editor.dart'; + +class AddNewRecentIncidentScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final incidentDateTEC = TextEditingController(); + final incidentTitleTEC = TextEditingController(); + + // TextEditingController incidentDetails = TextEditingController(); + QuillEditorController incidentDetailsQuillFieldController = + QuillEditorController(); + + final selectedDate = DateTime.now().obs; + final isButtonEnabled = false.obs; + + // DateTime? fromDateTime, toDateTime; + // RecentIncidentsModel recentIncidentsModel = RecentIncidentsModel.empty(); + + // String userId = ""; + late final AddNewRecentIncidentsScreenArgs arguments; + + AddNewRecentIncidentScreenController(AddNewRecentIncidentsScreenArgs args) { + arguments = args; + } + + @override + void onInit() { + incidentTitleTEC.addListener(_onIncidentTitleChanged); + selectedDate.listen(_setDateTimeInField); + + _setDateTimeInField(selectedDate()); + + fetchDataFromServiceAndSetToVariables(); + super.onInit(); + } + + _onIncidentTitleChanged() { + isButtonEnabled.value = incidentTitleTEC.text.isNotEmpty; + } + + _setDateTimeInField(DateTime d) { + incidentDateTEC.text = FrequentFunctions.careNoteDateFormatter.format(d); + } + + void fetchDataFromServiceAndSetToVariables() async { + if (arguments.incidentsModel != null) { + selectedDate.value = DateTime.fromMillisecondsSinceEpoch( + arguments.incidentsModel!.incidentDate); + incidentTitleTEC.text = arguments.incidentsModel!.incidentTitle; + await incidentDetailsQuillFieldController + .setText(arguments.incidentsModel!.note); + // isButtonEnabled.value = incidentTitleTEC.text.isNotEmpty; + } + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + selectDate(context) async { + Get.focusScope?.unfocus(); + final DateTime? d = await showDialog( + context: context, + builder: (BuildContext context) { + return AdoptiveCalendar( + initialDate: DateTime.now(), + action: true, + ); + }, + ); + + if (d != null) { + selectedDate.value = d; + // incidentDateTEC.text = FrequentFunctions.careNoteDateFormatter.format(d); + } + } + + // Future selectDateFromPicker( + // {required BuildContext context, + // required DateTime minDate, + // required DateTime maxDate}) async { + // try { + // final selected = await showDatePicker( + // context: context, + // initialDate: selectedDate.value, + // firstDate: minDate, + // lastDate: maxDate, + // // initialDate: minDate, + // // firstDate: DateTime.now(), + // // lastDate: DateTime(DateTime.now().year+1), + // // selectableDayPredicate: _showDatesToEnable, + // builder: (BuildContext context, Widget? child) { + // return Theme( + // data: ThemeData.light().copyWith( + // primaryColor: CustomAppColors.kSecondaryColor, + // colorScheme: const ColorScheme.light( + // primary: CustomAppColors.kSecondaryColor), + // buttonTheme: const ButtonThemeData( + // buttonColor: CustomAppColors.kSecondaryColor), + // ), + // child: child!, + // ); + // }); + // if (selected != null) { + // // updating selected date range based on selected week + // // setState(() { + // selectedDate.value = selected; + // // }); + // // widget.onDayPressed?.call( + // // selected, widget.markedDatesMap?.getEvents(selected) ?? const []); + // } + // } catch (e) { + // FrequentFunctions.showDialog( + // context: context, + // title: 'Alert', + // description: + // 'Something went wrong!! Please check your Date and Time Settings', + // type: DialogType.error, + // btnOkColor: CustomAppColors.kRedColor); + // } + // } + + Future submitButtonPressed(BuildContext context) async { + dynamic response; + if (arguments.userId.isNotNullOrEmpty()) { + response = await ClientService() + .addRecentIncidentService( + userId: arguments.userId!, + incidentTitle: incidentTitleTEC.text, + note: await incidentDetailsQuillFieldController.getText(), + incidentDate: selectedDate.value.millisecondsSinceEpoch, + ) + .showLoader(); + } else if (arguments.incidentsModel != null) { + response = await ClientService() + .updateRecentIncidentService( + incidentId: arguments.incidentsModel!.incidentId, + incidentNote: await incidentDetailsQuillFieldController.getText(), + incidentTitle: incidentTitleTEC.text, + incidentDate: selectedDate.value.millisecondsSinceEpoch, + ) + .showLoader(); + } + onBackPress(context, response: response); + } + + void onBackPress(BuildContext context, {dynamic response}) { + // Get.delete(); + Navigator.pop(context, response); + } + + @override + void dispose() { + incidentDateTEC.dispose(); + incidentTitleTEC.dispose(); + incidentTitleTEC.removeListener(_onIncidentTitleChanged); + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/clients/all_care_notes_screen_contorller.dart b/lib/controllers/clients/all_care_notes_screen_contorller.dart new file mode 100644 index 0000000..874649e --- /dev/null +++ b/lib/controllers/clients/all_care_notes_screen_contorller.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/allCareNotes/AllCareNotesListResponse.dart'; +import 'package:ftc_mobile_app/models/clients/allCareNotes/CarePlans.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; +import '../../utilities/frequent_functions.dart'; +import '../../web_services/client_services.dart'; + +class AllCareNotesScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final notesList = RxList(); + final canLoadMore = RxBool(false); + + int _total = 0; + final int _limit = 20; + int _skip = 0; + bool loadingMore = false; + + final _listRC = RefreshController(initialRefresh: false); + + RefreshController get listRC => _listRC; + + final _listSC = ScrollController(); + + ScrollController get listSC => _listSC; + + String serviceUserId = ""; + + @override + void onInit() { + super.onInit(); + + notesList.listen((list) { + canLoadMore.value = list.length < _total; + }); + } + + void onRefresh() async { + await getCareNotesList(); + _listRC.refreshCompleted(); + } + + void onLoading() async { + if (!loadingMore) { + await _loadMore(); + } + + _listRC.loadComplete(); + } + + Future getCareNotesList() async { + _skip = 0; + + var response = await ClientService() + .getCarePlansList( + serviceUserId: serviceUserId, + limit: _limit, + offset: _skip, + ) + .showLoader(); + if (response is AllCareNotesListResponse) { + if (response.data?.carePlans?.isNotEmpty == true) { + _total = response.data?.carePlanCount ?? 0; + _skip += _limit; + notesList.value = (response.data?.carePlans ?? []) + ..sort((a, b) { + if (b.flag == true) { + return 1; + } + return -1; + }); + } else { + notesList.clear(); + } + } else if (response is String && response.isNotEmpty) { + notesList.clear(); + FrequentFunctions.showToast(message: response); + } else { + notesList.clear(); + } + } + + Future _loadMore() async { + if (canLoadMore.isTrue) { + loadingMore = true; + + var response = await ClientService().getCarePlansList( + serviceUserId: serviceUserId, + limit: _limit, + offset: _skip, + ); + loadingMore = false; + + if (response is AllCareNotesListResponse) { + if (response.data?.carePlans?.isNotEmpty == true) { + _skip += _limit; + notesList.addAll(response.data?.carePlans ?? []); + } + } else if (response is String && response.isNotEmpty) { + FrequentFunctions.showToast(message: response); + } + } + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + + void backButtonPressed(BuildContext context) { + Get.delete(); + Navigator.of(context).pop(); + } +} diff --git a/lib/controllers/clients/appointment_screen_controller.dart b/lib/controllers/clients/appointment_screen_controller.dart new file mode 100644 index 0000000..e85fb0d --- /dev/null +++ b/lib/controllers/clients/appointment_screen_controller.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; +import '../../ftc_mobile_app.dart'; +import '../../models/appointmentsListResponse/AppointmentsListResponse.dart'; + +class AppointmentScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final appointments = [].obs; + + final _listRC = RefreshController(initialRefresh: false); + + RefreshController get listRC => _listRC; + + final _listSC = ScrollController(); + + ScrollController get listSC => _listSC; + + String serviceUserId = ""; + + @override + void onReady() { + super.onReady(); + + getAppointmentsList(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + void onRefresh() async { + await getAppointmentsList(); + _listRC.refreshCompleted(); + } + + Future getAppointmentsList() async { + try { + if (serviceUserId.isEmpty) { + throw Exception("serviceUserId is not assigned"); + } + + final response = await ClientService() + .getAppointmentsList( + serviceId: serviceUserId, + startDate: DateTime.now().subtract(28.days).millisecondsSinceEpoch, + endDate: DateTime.now().add(28.days).millisecondsSinceEpoch, + ) + .showLoader(); + if (response.success == true) { + appointments.value = response.data ?? []; + } else { + if (response.message.isNotNullOrEmpty()) { + FrequentFunctions.showToast(message: response.message!); + } + } + } catch (e) { + debugPrint("getAppointmentsList error"); + debugPrint(e.toString()); + } + } + + @override + void dispose() { + _listSC.dispose(); + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/ABC_form_screen_controller.dart b/lib/controllers/clients/careNoteFormControllers/ABC_form_screen_controller.dart new file mode 100644 index 0000000..2e35cf5 --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/ABC_form_screen_controller.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/careNoteFormsRequests/ABC_form_html_request.dart'; +import 'package:get/get.dart'; +import 'common_care_note_forms_controller.dart'; + +class ABCFormScreenController extends CommonCareNoteFormsController { + final antecedentEventsController = TextEditingController(); + final behaviourController = TextEditingController(); + final consequenceEventsController = TextEditingController(); + + ABCFormScreenController({required CommonCareNoteFormArgs args}) + : super(args: args); + + @override + void dispose() { + antecedentEventsController.dispose(); + behaviourController.dispose(); + consequenceEventsController.dispose(); + Get.delete(); + super.dispose(); + } + + Future onSaveButtonTap() async { + // if (super.date == null || super.time == null) { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + if (antecedentEventsController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Antecedent Events field is required", + ); + return; + } + if (behaviourController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Behaviour field is required", + ); + return; + } + + if (consequenceEventsController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Consequence Events field is required", + ); + return; + } + + super.removeFocus(); + + await onFormSubmit( + request: CreateCarePlanRequest( + isHTML: true, + flag: false, + title: "", + noteDetails: ABCFormHtmlRequest( + antecedentEvents: antecedentEventsController.text.trim(), + behaviour: behaviourController.text.trim(), + consequenceEvents: consequenceEventsController.text.trim(), + ).toHtml()), + ); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/category_subcategory_widget_controller.dart b/lib/controllers/clients/careNoteFormControllers/category_subcategory_widget_controller.dart new file mode 100644 index 0000000..0d67939 --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/category_subcategory_widget_controller.dart @@ -0,0 +1,46 @@ +import 'dart:developer'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/careNoteFormsRequests/physical_intervention_form_html_request.dart'; +import 'package:ftc_mobile_app/models/clients/careNoteFormsRequests/safeguarding_form_html_request.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/web_services/client_services.dart'; +import 'package:get/get.dart'; +import '../../../models/clients/body_points_category.dart'; +import 'common_care_note_forms_controller.dart'; + +class CategorySubcategoryWidgetController extends GetxController { + final bodyPointsCategoryList = RxList(); + final selectedBodyPart = Rx(null); + final selectedSubcategory = Rx(null); + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + + getBodyParts() async { + var result = await ClientService().getBodyPointsCategoryList().showLoader(); + if (result is List && result is List) { + log('---------------bodyPointsCategoryList length before set -----------${bodyPointsCategoryList.length}'); + + List uniqueList = removeDuplicates(result); + bodyPointsCategoryList.value = uniqueList; + // selectedBodyPart.value = bodyPointsCategoryList.first; + } + } + + List removeDuplicates(List list) { + List uniqueList = []; + Set enumedSet = {}; + + for (var category in list) { + if (!enumedSet.contains(category.idOne)) { + uniqueList.add(category); + enumedSet.add(category.idOne); + } + } + + return uniqueList; + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart b/lib/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart new file mode 100644 index 0000000..aabb298 --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart @@ -0,0 +1,89 @@ +import 'dart:convert'; +import 'package:adoptive_calendar/adoptive_calendar.dart'; +import 'package:ftc_mobile_app/models/response_model.dart'; +import 'package:ftc_mobile_app/models/user_model.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/web_services/client_services.dart'; +import 'package:get/get.dart'; +import '../../../models/create_care_plan_request.dart'; +import 'package:flutter/material.dart'; +import '../../../utilities/export_utilities.dart'; + +export '../../../utilities/export_utilities.dart'; +export '../../../models/create_care_plan_request.dart'; + +class CommonCareNoteFormArgs { + final String serviceUserId; + final String noteType; + + CommonCareNoteFormArgs({required this.serviceUserId, required this.noteType}); +} + +abstract class CommonCareNoteFormsController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final dateController = TextEditingController(); + + DateTime? date; + + final CommonCareNoteFormArgs args; + + CommonCareNoteFormsController({required this.args}); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + selectDate(context) async { + Get.focusScope?.unfocus(); + final DateTime? d = await showDialog( + context: context, + builder: (BuildContext context) { + return AdoptiveCalendar( + initialDate: DateTime.now(), + action: true, + ); + }, + ); + + if (d != null) { + date = d; + dateController.text = FrequentFunctions.careNoteDateFormatter.format(d); + } + } + + Future onFormSubmit({required CreateCarePlanRequest request}) async { + // final userJson = LocalStorageManager.getSessionToken( + // tokenKey: LocalStorageKeys.kUserModelKey, + // ); + // print("userJson: $userJson"); + // final userModel = UserModel.fromJson(jsonDecode(userJson)); + request.addedby = LocalStorageManager.userId; + request.userId = args.serviceUserId; + request.noteType = args.noteType; + if (date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + request.eventDateTime = date!.toUtc().millisecondsSinceEpoch; + + final response = + await ClientService().createCarePlan(request: request).showLoader(); + + if (response is ResponseModel) { + Navigator.pop(screenKey.currentContext!); + Navigator.pop(screenKey.currentContext!); + FrequentFunctions.showToast(message: response.statusDescription); + } else { + FrequentFunctions.showToast(message: response['message']); + } + } + + @override + void dispose() { + dateController.dispose(); + super.dispose(); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/consent_capacity_form_screen_controller.dart b/lib/controllers/clients/careNoteFormControllers/consent_capacity_form_screen_controller.dart new file mode 100644 index 0000000..388392c --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/consent_capacity_form_screen_controller.dart @@ -0,0 +1,285 @@ +import 'package:adoptive_calendar/adoptive_calendar.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import '../../../models/clients/careNoteFormsRequests/HtmlTableOption.dart'; +import '../../../models/clients/careNoteFormsRequests/consent_capacity_html_request.dart'; +import 'common_care_note_forms_controller.dart'; +import 'package:flutter/services.dart' show rootBundle; + +class ConsentCapacityFormScreenController + extends CommonCareNoteFormsController { + final commentsController = TextEditingController(); + final mentalCapacityAssessmentDetailController = TextEditingController(); + final specificDecisionDetailController = TextEditingController(); + final roleController = TextEditingController(); + final organisationController = TextEditingController(); + final addressController = TextEditingController(); + final telController = TextEditingController(); + final emailController = TextEditingController(); + final lackCapacityToMakeParticularDecisionDetailController = + TextEditingController(); + final recordYourEvidenceDescribeController = TextEditingController(); + final viableOptionsConsideredController = TextEditingController(); + final explainWhyTickedBoxController = TextEditingController(); + final impairmentDescribeController = TextEditingController(); + final describeCanPersonDecisionInfoController = TextEditingController(); + final describeCanTheyRetainController = TextEditingController(); + final describeCanTheyUseController = TextEditingController(); + final describeCanTheyCommunicateController = TextEditingController(); + final evidenceController = TextEditingController(); + final whatIsYourEvidenceController = TextEditingController(); + final seekingManagementDescribeController = TextEditingController(); + final recordInterviewDescribeController = TextEditingController(); + final requireIMCAController = TextEditingController(); + final designationController = TextEditingController(); + final baseAddressController = TextEditingController(); + final contactDetailsController = TextEditingController(); + + final name1Controller = TextEditingController(); + final dontHaveDecisionNameController = TextEditingController(); + final haveDecisionNameController = TextEditingController(); + final assessorsName4Controller = TextEditingController(); + + final assessmentDateTimeController = TextEditingController(); + final dontHaveDecisionDateController = TextEditingController(); + final haveDecisionDateController = TextEditingController(); + final whyIMCARequiredDateController = TextEditingController(); + + //Yes/No radio selected options + final selectedMCARequiredOption = Rx(null); + final selectedImpairmentOption = Rx(null); + final selectedCanPersonDecisionInfoOption = Rx(null); + final selectedCanTheyRetainOption = Rx(null); + final selectedCanTheyUseOption = Rx(null); + final selectedCanTheyCommunicateOption = Rx(null); + final selectedDoYouHaveConcernOption = Rx(null); + final selectedDoesRequireIMCAOption = Rx("No"); + + List canDecisionBeDelayedOptions = [ + HtmlTableOption(id: '1', requirements: 'The decision can be delayed'), + HtmlTableOption( + id: '2', requirements: 'Not appropriate to delay the decision'), + HtmlTableOption( + id: '3', + requirements: 'Person not likely to gain or develop capacity '), + ]; + + List causativeNexusOptions = [ + HtmlTableOption(id: '1', requirements: 'Yes, there is a causative link '), + HtmlTableOption( + id: '2', + requirements: + 'No, there is not a causative link, so the person has capacity to make the relevant decision. The decision may therefore be an unwise decision. '), + ]; + + DateTime? assessmentDateTime; + DateTime? dontHaveDecisionDateTime; + DateTime? haveDecisionDateTime; + DateTime? whyIMCARequiredDateTime; + + String consentCapacityHtml = ""; + + ConsentCapacityFormScreenController({required CommonCareNoteFormArgs args}) + : super(args: args); + + @override + void onInit() { + loadConsentCapacityHtmlFile(); + super.onInit(); + } + + @override + void dispose() { + commentsController.dispose(); + mentalCapacityAssessmentDetailController.dispose(); + specificDecisionDetailController.dispose(); + roleController.dispose(); + organisationController.dispose(); + addressController.dispose(); + telController.dispose(); + emailController.dispose(); + assessmentDateTimeController.dispose(); + lackCapacityToMakeParticularDecisionDetailController.dispose(); + recordYourEvidenceDescribeController.dispose(); + viableOptionsConsideredController.dispose(); + explainWhyTickedBoxController.dispose(); + impairmentDescribeController.dispose(); + describeCanPersonDecisionInfoController.dispose(); + describeCanTheyRetainController.dispose(); + describeCanTheyUseController.dispose(); + describeCanTheyCommunicateController.dispose(); + evidenceController.dispose(); + whatIsYourEvidenceController.dispose(); + seekingManagementDescribeController.dispose(); + recordInterviewDescribeController.dispose(); + requireIMCAController.dispose(); + designationController.dispose(); + baseAddressController.dispose(); + contactDetailsController.dispose(); + + name1Controller.dispose(); + dontHaveDecisionNameController.dispose(); + haveDecisionNameController.dispose(); + assessorsName4Controller.dispose(); + + dontHaveDecisionDateController.dispose(); + haveDecisionDateController.dispose(); + whyIMCARequiredDateController.dispose(); + + Get.delete(); + super.dispose(); + } + + Future loadConsentCapacityHtmlFile() async { + final htmlString = + await rootBundle.loadString(AssetsManager.kConsentCapacityFormHtml); + consentCapacityHtml = htmlString; + } + + Future _unFocusAndPickDate(BuildContext context) async { + Get.focusScope?.unfocus(); + return await showDialog( + context: context, + builder: (BuildContext context) { + return AdoptiveCalendar( + initialDate: DateTime.now(), + action: true, + ); + }, + ); + } + + selectAssessmentDateTime(BuildContext context) async { + final d = await _unFocusAndPickDate(context); + + if (d != null) { + assessmentDateTime = d; + assessmentDateTimeController.text = + FrequentFunctions.careNoteDateFormatter.format(d); + } + } + + selectDontHaveDecisionDateTime(BuildContext context) async { + final d = await _unFocusAndPickDate(context); + + if (d != null) { + dontHaveDecisionDateTime = d; + dontHaveDecisionDateController.text = + FrequentFunctions.careNoteDateFormatter.format(d); + } + } + + selectHaveDecisionDateTime(BuildContext context) async { + final d = await _unFocusAndPickDate(context); + + if (d != null) { + haveDecisionDateTime = d; + haveDecisionDateController.text = + FrequentFunctions.careNoteDateFormatter.format(d); + } + } + + selectDateTimeOfWhyIMCARequired(BuildContext context) async { + final d = await _unFocusAndPickDate(context); + + if (d != null) { + whyIMCARequiredDateTime = d; + whyIMCARequiredDateController.text = + FrequentFunctions.careNoteDateFormatter.format(d); + } + } + + Future onSaveButtonTap() async { + // if (super.date == null || super.time == null) { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + + if (commentsController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Comments field is required", + ); + return; + } + + super.removeFocus(); + final formatter = DateFormat("dd/MM/yyyy / hh:mm aa"); + + final consentCapacityHtmlString = ConsentCapacityHtmlRequest( + isMCARequired: selectedMCARequiredOption()!, + comments: commentsController.text.trim(), + mentalCapacityAssessmentDetail: + mentalCapacityAssessmentDetailController.text.trim(), + specificDecisionDetail: specificDecisionDetailController.text.trim(), + personUndertakingName: name1Controller.text.trim(), + personUndertakingRole: roleController.text.trim(), + organisation: organisationController.text.trim(), + address: addressController.text.trim(), + tel: telController.text.trim(), + email: emailController.text.trim(), + dateAndTimeOfAssessment: (assessmentDateTime != null) + ? formatter.format(assessmentDateTime!) + : "", + lackCapacityToMakeParticularDecisionDetail: + lackCapacityToMakeParticularDecisionDetailController.text.trim(), + recordYourEvidenceDescribe: + recordYourEvidenceDescribeController.text.trim(), + viableOptionsConsidered: viableOptionsConsideredController.text.trim(), + explainWhyTickedBox: explainWhyTickedBoxController.text.trim(), + canDecisionBeDelayedOptions: canDecisionBeDelayedOptions, + selectedImpairmentOption: selectedImpairmentOption() ?? "", + impairmentDescribe: impairmentDescribeController.text.trim(), + selectedCanPersonDecisionInfoOption: + selectedCanPersonDecisionInfoOption() ?? "", + describeCanPersonDecisionInfo: + describeCanPersonDecisionInfoController.text.trim(), + selectedCanTheyRetainOption: selectedCanTheyRetainOption() ?? "", + describeCanTheyRetain: describeCanTheyRetainController.text.trim(), + selectedCanTheyUseOption: selectedCanTheyUseOption() ?? "", + describeCanTheyUse: describeCanTheyUseController.text.trim(), + selectedCanTheyCommunicateOption: + selectedCanTheyCommunicateOption() ?? "", + describeCanTheyCommunicate: + describeCanTheyCommunicateController.text.trim(), + causativeNexusOptions: causativeNexusOptions, + evidence: evidenceController.text.trim(), + selectedDoYouHaveConcernOption: selectedDoYouHaveConcernOption() ?? "", + whatIsYourEvidence: whatIsYourEvidenceController.text.trim(), + seekingManagementDescribe: + seekingManagementDescribeController.text.trim(), + recordInterviewDescribe: recordInterviewDescribeController.text.trim(), + section9DontHaveDecisionNameData: + dontHaveDecisionNameController.text.trim(), + section9DontHaveDecisionDateData: (dontHaveDecisionDateTime != null) + ? formatter.format(dontHaveDecisionDateTime!) + : "", + section9HaveDecisionNameData: haveDecisionNameController.text.trim(), + section9HaveDecisionDateData: (haveDecisionDateTime != null) + ? formatter.format(haveDecisionDateTime!) + : "", + isIMCARequired: selectedDoesRequireIMCAOption(), + giveWhyIMCARequiredReason: requireIMCAController.text.trim(), + whyIMCARequiredDateTime: (whyIMCARequiredDateTime != null) + ? formatter.format(whyIMCARequiredDateTime!) + : "", + section9AssessorsName: assessorsName4Controller.text.trim(), + assessorsDesignation: designationController.text.trim(), + assessorsBaseAddress: baseAddressController.text.trim(), + assessorsContactDetailsData: contactDetailsController.text.trim(), + ).toHtml(consentCapacityHtml); + + // log("consentCapacityHtmlString: $consentCapacityHtmlString"); + await onFormSubmit( + request: CreateCarePlanRequest( + isHTML: true, + flag: false, + title: "", + noteDetails: consentCapacityHtmlString, + ), + ); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/free_text_entries_form_screen_controller.dart b/lib/controllers/clients/careNoteFormControllers/free_text_entries_form_screen_controller.dart new file mode 100644 index 0000000..30f5b5d --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/free_text_entries_form_screen_controller.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'common_care_note_forms_controller.dart'; + +class FreeTextEntriesFormScreenController + extends CommonCareNoteFormsController { + final titleController = TextEditingController(); + final noteDetailsController = TextEditingController(); + + final flagForHandover = false.obs; + + FreeTextEntriesFormScreenController({required CommonCareNoteFormArgs args}) + : super(args: args); + + @override + void dispose() { + titleController.dispose(); + noteDetailsController.dispose(); + Get.delete(); + super.dispose(); + } + + Future onSaveButtonTap() async { + // if (super.date == null || super.time == null) { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + if (titleController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Title field is required", + ); + return; + } + if (noteDetailsController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Note Details field is required", + ); + return; + } + + super.removeFocus(); + + await onFormSubmit( + request: CreateCarePlanRequest( + title: titleController.text.trim(), + noteDetails: noteDetailsController.text.trim(), + flag: flagForHandover(), + ), + ); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/health_appointments_form_screen_controller.dart b/lib/controllers/clients/careNoteFormControllers/health_appointments_form_screen_controller.dart new file mode 100644 index 0000000..3a29b18 --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/health_appointments_form_screen_controller.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/careNoteFormsRequests/health_appointments_form_html_request.dart'; +import 'package:get/get.dart'; +import 'common_care_note_forms_controller.dart'; + +class HealthAppointmentsFormScreenController + extends CommonCareNoteFormsController { + final reasonController = TextEditingController(); + final commentsController = TextEditingController(); + + final appointmentWith = [ + "GP", + "CAMHS", + "Psychologist", + "A&E", + "Sexual Health", + "Social Worker", + "Other" + ]; + + String? selectedAppointmentWith; + + HealthAppointmentsFormScreenController({required CommonCareNoteFormArgs args}) + : super(args: args); + + @override + void dispose() { + reasonController.dispose(); + commentsController.dispose(); + Get.delete(); + super.dispose(); + } + + Future onSaveButtonTap() async { + // if (super.date == null || super.time == null) { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + if (selectedAppointmentWith == null) { + FrequentFunctions.showToast( + message: "Please select appointment with", + ); + return; + } + if (reasonController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Reason for appointment field is required", + ); + return; + } + if (commentsController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Comments field is required", + ); + return; + } + + super.removeFocus(); + + await onFormSubmit( + request: CreateCarePlanRequest( + isHTML: true, + flag: false, + title: "", + noteDetails: HealthAppointmentsFormHtmlRequest( + appointmentWith: selectedAppointmentWith!, + reason: reasonController.text.trim(), + comments: commentsController.text.trim(), + ).toHtml(), + ), + ); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/injury_health_issue_form_screen_controller.dart b/lib/controllers/clients/careNoteFormControllers/injury_health_issue_form_screen_controller.dart new file mode 100644 index 0000000..acd8dc1 --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/injury_health_issue_form_screen_controller.dart @@ -0,0 +1,152 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'category_subcategory_widget_controller.dart'; +import 'common_care_note_forms_controller.dart'; + +class InjuryHealthIssueFormScreenController + extends CommonCareNoteFormsController { + final nameOfWitnesses = TextEditingController(); + final placeOfAccident = TextEditingController(); + final accidentDescription = TextEditingController(); + final recordOfInjury = TextEditingController(); + final conditionOfPatient = TextEditingController(); + final nameOfParentContacted = TextEditingController(); + final parentContactedTime = TextEditingController(); + + final isParentContacted = RxString("No"); + final howParentContacted = Rx(null); + + final catSubCatController = Get.put(CategorySubcategoryWidgetController()); + + InjuryHealthIssueFormScreenController({required CommonCareNoteFormArgs args}) + : super(args: args); + + @override + void onReady() { + catSubCatController.getBodyParts(); + super.onReady(); + } + + @override + void dispose() { + nameOfWitnesses.dispose(); + placeOfAccident.dispose(); + accidentDescription.dispose(); + recordOfInjury.dispose(); + conditionOfPatient.dispose(); + nameOfParentContacted.dispose(); + parentContactedTime.dispose(); + + catSubCatController.dispose(); + + Get.delete(); + super.dispose(); + } + + selectParentContactTime(context) async { + Get.focusScope?.unfocus(); + TimeOfDay? timeOfDay = await FrequentFunctions.selectTime(context, + selectedTime: TimeOfDay.now(), + themeColor: Get.theme.colorScheme.primary); + + if (timeOfDay != null) { + parentContactedTime.text = timeOfDay.format(context); + } + } + + Future onSaveButtonTap() async { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + + if (nameOfWitnesses.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Name of witnesses/adults present field is required", + ); + return; + } + + if (placeOfAccident.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Place accident occured field is required", + ); + return; + } + + if (accidentDescription.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Description how the accident occured field is required", + ); + return; + } + + if (catSubCatController.selectedBodyPart() == null) { + FrequentFunctions.showToast( + message: "Please select category first", + ); + return; + } + + if (catSubCatController.selectedBodyPart()!.subCategory.isNotEmpty && + catSubCatController.selectedSubcategory() == null) { + FrequentFunctions.showToast( + message: "Please select subcategory", + ); + return; + } + + if (isParentContacted() == "Yes" && + nameOfParentContacted.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Name of parent contacted field is required", + ); + return; + } + + if (isParentContacted() == "Yes" && + parentContactedTime.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Please select parent contacted time", + ); + return; + } + + if (isParentContacted() == "Yes" && howParentContacted() == null) { + FrequentFunctions.showToast( + message: "Please select how parent was contacted", + ); + return; + } + + super.removeFocus(); + + await onFormSubmit( + request: CreateCarePlanRequest( + isHTML: true, + flag: false, + title: "", + category: catSubCatController.selectedSubcategory()?.id ?? + catSubCatController.selectedBodyPart()!.id, + noteDetails: CreateCarePlanRequest.injuryHealthIssueHtmlReq( + nameOfWitnesses: nameOfWitnesses.text.trim(), + placeOfAccident: placeOfAccident.text.trim(), + accidentDescription: accidentDescription.text.trim(), + recordOfInjury: recordOfInjury.text.trim(), + conditionOfPatient: conditionOfPatient.text.trim(), + isParentContacted: isParentContacted(), + nameOfParentContacted: isParentContacted() != "Yes" + ? null + : nameOfParentContacted.text.trim(), + parentContactedTime: isParentContacted() != "Yes" + ? null + : parentContactedTime.text.trim(), + howParentContacted: + isParentContacted() != "Yes" ? null : howParentContacted()!, + ), + ), + ); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/mood_rating_form_controller.dart b/lib/controllers/clients/careNoteFormControllers/mood_rating_form_controller.dart new file mode 100644 index 0000000..cf7f550 --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/mood_rating_form_controller.dart @@ -0,0 +1,60 @@ +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import '../../../models/mood_rating_data.dart'; +import 'common_care_note_forms_controller.dart'; + +class MoodRatingFormController extends CommonCareNoteFormsController { + MoodRatingFormController({required super.args}); + + final ratings = [ + MoodRatingData(icon: AssetsManager.ratingsIcAngry, name: 'Angry'), + MoodRatingData(icon: AssetsManager.ratingsIcBored, name: 'Bored'), + MoodRatingData(icon: AssetsManager.ratingsIcCalm, name: 'Calm'), + MoodRatingData(icon: AssetsManager.ratingsIcConfident, name: 'Confident'), + MoodRatingData(icon: AssetsManager.ratingsIcExcited, name: 'Excited'), + MoodRatingData(icon: AssetsManager.ratingsIcHappy, name: 'Happy'), + MoodRatingData(icon: AssetsManager.ratingsIcHopeful, name: 'Hopeful'), + MoodRatingData(icon: AssetsManager.ratingsIcNervous, name: 'Nervous'), + MoodRatingData(icon: AssetsManager.ratingsIcProud, name: 'Proud'), + MoodRatingData(icon: AssetsManager.ratingsIcRelaxed, name: 'Relaxed'), + MoodRatingData(icon: AssetsManager.ratingsIcSad, name: 'Sad'), + MoodRatingData(icon: AssetsManager.ratingsIcScared, name: 'Scared'), + MoodRatingData(icon: AssetsManager.ratingsIcTired, name: 'Tired'), + MoodRatingData(icon: AssetsManager.ratingsIcWorried, name: 'Worried'), + ]; + + final Rx selectedRating = Rx(null); + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + + Future onSaveButtonTap() async { + // if (super.date == null || super.time == null) { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + if (selectedRating() == null) { + FrequentFunctions.showToast( + message: "Please select your mood rating", + ); + return; + } + + super.removeFocus(); + + await onFormSubmit( + request: CreateCarePlanRequest( + title: "Mood Rating is ${selectedRating()!.name}", + noteDetails: + "Mood Rating is ${selectedRating()!.name} at ${DateFormat("hh:mm aa on dd/MM/yyyy").format(date!)}", + moodRating: selectedRating()!.name, + ), + ); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/nutrition_hydration_form_screen_controller.dart b/lib/controllers/clients/careNoteFormControllers/nutrition_hydration_form_screen_controller.dart new file mode 100644 index 0000000..1ed712b --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/nutrition_hydration_form_screen_controller.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/careNoteFormsRequests/nutrition_hydration_form_html_request.dart'; +import 'package:get/get.dart'; +import 'common_care_note_forms_controller.dart'; + +abstract class NutritionHydrationType { + static const String food = "Food"; + static const String fluid = "Fluid"; +} + +class NutritionHydrationFormScreenController + extends CommonCareNoteFormsController { + final mealDrinkTypeController = TextEditingController(); + final amountController = TextEditingController(); + final commentsController = TextEditingController(); + + final selectedType = RxString(NutritionHydrationType.food); + + final typeOptions = [ + NutritionHydrationType.food, + NutritionHydrationType.fluid + ]; + + NutritionHydrationFormScreenController({required CommonCareNoteFormArgs args}) + : super(args: args); + + @override + void dispose() { + mealDrinkTypeController.dispose(); + amountController.dispose(); + commentsController.dispose(); + Get.delete(); + super.dispose(); + } + + Future onSaveButtonTap() async { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + + if (commentsController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Comments field is required", + ); + return; + } + + super.removeFocus(); + + await onFormSubmit( + request: CreateCarePlanRequest( + isHTML: true, + flag: false, + title: "", + noteDetails: NutritionHydrationFormHtmlRequest( + nutritionHydrationType: selectedType(), + foodFluidType: mealDrinkTypeController.text.trim(), + foodFluidAmount: amountController.text.trim(), + comments: commentsController.text.trim(), + ).toHtml(), + ), + ); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/observations_form_screen_controller.dart b/lib/controllers/clients/careNoteFormControllers/observations_form_screen_controller.dart new file mode 100644 index 0000000..857830d --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/observations_form_screen_controller.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/careNoteFormsRequests/observations_form_html_request.dart'; +import 'package:get/get.dart'; +import 'common_care_note_forms_controller.dart'; + +class ObservationsFormScreenController extends CommonCareNoteFormsController { + final heartRateController = TextEditingController(); + final bloodPressureController = TextEditingController(); + final respiratoryRateController = TextEditingController(); + final oxygenController = TextEditingController(); + final temperatureController = TextEditingController(); + final bloodSugarController = TextEditingController(); + + ObservationsFormScreenController({required CommonCareNoteFormArgs args}) + : super(args: args); + + @override + void dispose() { + heartRateController.dispose(); + bloodPressureController.dispose(); + respiratoryRateController.dispose(); + oxygenController.dispose(); + temperatureController.dispose(); + bloodSugarController.dispose(); + + Get.delete(); + super.dispose(); + } + + Future onSaveButtonTap() async { + // if (super.date == null || super.time == null) { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + if (heartRateController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Heart Rate field is required", + ); + return; + } + if (bloodPressureController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Blood Pressure field is required", + ); + return; + } + if (respiratoryRateController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Respiratory Rate field is required", + ); + return; + } + if (oxygenController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Oxygen field is required", + ); + return; + } + if (temperatureController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Temperature field is required", + ); + return; + } + if (bloodSugarController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Blood Sugar field is required", + ); + return; + } + + super.removeFocus(); + + await onFormSubmit( + request: CreateCarePlanRequest( + isHTML: true, + flag: false, + title: "", + noteDetails: ObservationsFormHtmlReq( + heartRate: heartRateController.text.trim(), + bloodPressure: bloodPressureController.text.trim(), + respiratoryRate: respiratoryRateController.text.trim(), + oxygen: oxygenController.text.trim(), + temperature: temperatureController.text.trim(), + bloodSugar: bloodSugarController.text.trim(), + ).toHtml(), + ), + ); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/physical_intervention_form_screen_controller.dart b/lib/controllers/clients/careNoteFormControllers/physical_intervention_form_screen_controller.dart new file mode 100644 index 0000000..87df2db --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/physical_intervention_form_screen_controller.dart @@ -0,0 +1,209 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/careNoteFormsRequests/physical_intervention_form_html_request.dart'; +import 'package:get/get.dart'; +import '../../../models/clients/careNoteFormsRequests/HtmlTableOption.dart'; +import 'category_subcategory_widget_controller.dart'; +import 'common_care_note_forms_controller.dart'; + +class PhysicalInterventionFormScreenController + extends CommonCareNoteFormsController { + final durationOfIncidentController = TextEditingController(); + final staffDebriefFormNumberController = TextEditingController(); + final nameOfWitnessController = TextEditingController(); + final incidentPlaceController = TextEditingController(); + final whatWasUsedController = TextEditingController(); + final wasThePbsFollowedController = TextEditingController(); + final reasonForPhysicalInterventionController = TextEditingController(); + final staffInvolvedController = TextEditingController(); + final conditionOfServiceUserController = TextEditingController(); + final userClamedController = TextEditingController(); + final explainController = TextEditingController(); + final commentsController = TextEditingController(); + + final nameOfParentContacted = TextEditingController(); + final parentContactedTime = TextEditingController(); + + final isParentContacted = RxString("No"); + final howParentContacted = Rx(null); + final howFormSharedRx = Rx(null); + + final catSubCatController = Get.put(CategorySubcategoryWidgetController()); + + List whyForceNecessaryOptions = [ + HtmlTableOption( + id: '1', requirements: 'Service User was placing themselves at risk'), + HtmlTableOption( + id: '2', requirements: 'Service User was placing others at risk'), + HtmlTableOption( + id: '3', requirements: 'Significant Damage to property'), + HtmlTableOption( + id: '4', requirements: 'Illegal offence was being carried out'), + HtmlTableOption(id: '5', requirements: 'Other'), + ]; + + final isParentContactedOptions = ['Yes', 'No']; + final howParentContactedOptions = [ + 'Call', + 'Email', + 'Other', + 'Upon collection/drop off' + ]; + final howFormSharedOptions = ['Paper', 'Email', 'Other']; + + PhysicalInterventionFormScreenController( + {required CommonCareNoteFormArgs args}) + : super(args: args); + + @override + void onReady() { + catSubCatController.getBodyParts(); + super.onReady(); + } + + @override + void dispose() { + durationOfIncidentController.dispose(); + staffDebriefFormNumberController.dispose(); + nameOfWitnessController.dispose(); + incidentPlaceController.dispose(); + whatWasUsedController.dispose(); + wasThePbsFollowedController.dispose(); + reasonForPhysicalInterventionController.dispose(); + staffInvolvedController.dispose(); + conditionOfServiceUserController.dispose(); + userClamedController.dispose(); + explainController.dispose(); + commentsController.dispose(); + nameOfParentContacted.dispose(); + parentContactedTime.dispose(); + + catSubCatController.dispose(); + + Get.delete(); + super.dispose(); + } + + selectParentContactTime(context) async { + Get.focusScope?.unfocus(); + TimeOfDay? timeOfDay = await FrequentFunctions.selectTime(context, + selectedTime: TimeOfDay.now(), + themeColor: Get.theme.colorScheme.primary); + + if (timeOfDay != null) { + parentContactedTime.text = timeOfDay.format(context); + } + } + + Future onSaveButtonTap() async { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + + if (durationOfIncidentController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Concerns about the service user field is required", + ); + return; + } + + if (staffDebriefFormNumberController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Voice of the service user field is required", + ); + return; + } + + if (nameOfWitnessController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Are there any immediate risks field is required", + ); + return; + } + + if (catSubCatController.selectedBodyPart() == null) { + FrequentFunctions.showToast( + message: "Please select category first", + ); + return; + } + + if (catSubCatController.selectedBodyPart()!.subCategory.isNotEmpty && + catSubCatController.selectedSubcategory() == null) { + FrequentFunctions.showToast( + message: "Please select subcategory", + ); + return; + } + if (isParentContacted() == "Yes" && + nameOfParentContacted.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Name of parent contacted field is required", + ); + return; + } + + if (isParentContacted() == "Yes" && + parentContactedTime.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Please select parent contacted time", + ); + return; + } + + if (isParentContacted() == "Yes" && howParentContacted() == null) { + FrequentFunctions.showToast( + message: "Please select how parent was contacted", + ); + return; + } + + if (howFormSharedRx() == null) { + FrequentFunctions.showToast( + message: "Please select How was this form shared with parents/carers?", + ); + return; + } + + super.removeFocus(); + + await onFormSubmit( + request: CreateCarePlanRequest( + isHTML: true, + flag: false, + title: "", + category: catSubCatController.selectedSubcategory()?.id ?? + catSubCatController.selectedBodyPart()!.id, + noteDetails: PhysicalInterventionFormHtmlRequest( + durationOfIncidents: durationOfIncidentController.text.trim(), + staffDebriefFormNumber: + staffDebriefFormNumberController.text.trim(), + nameOfWitnesses: nameOfWitnessController.text.trim(), + placeOfIncident: incidentPlaceController.text.trim(), + priorToIntervention: whatWasUsedController.text.trim(), + pbsFollowed: wasThePbsFollowedController.text.trim(), + reasonForPhysicalIntervention: + reasonForPhysicalInterventionController.text.trim(), + staffInvolvedInPI: staffInvolvedController.text.trim(), + conditionOfSU: conditionOfServiceUserController.text.trim(), + howSuCalmed: userClamedController.text.trim(), + useOfForceNecessary: whyForceNecessaryOptions, + pleaseExplain: explainController.text.trim(), + isParentContacted: isParentContacted(), + nameOfParentContacted: isParentContacted() != "Yes" + ? "" + : nameOfParentContacted.text.trim(), + parentContactedTime: isParentContacted() != "Yes" + ? "" + : parentContactedTime.text.trim(), + howParentContacted: + isParentContacted() != "Yes" ? "" : howParentContacted()!, + parentCarersComments: commentsController.text.trim(), + howFormWasShared: howFormSharedRx()!) + .toHtml(), + ), + ); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/safeguarding_form_screen_controller.dart b/lib/controllers/clients/careNoteFormControllers/safeguarding_form_screen_controller.dart new file mode 100644 index 0000000..fd8cc20 --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/safeguarding_form_screen_controller.dart @@ -0,0 +1,138 @@ +import 'package:adoptive_calendar/adoptive_calendar.dart'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/careNoteFormsRequests/safeguarding_form_html_request.dart'; +import 'package:get/get.dart'; +import 'category_subcategory_widget_controller.dart'; +import 'common_care_note_forms_controller.dart'; + +class SafeguardingFormScreenController extends CommonCareNoteFormsController { + final concernAboutServiceUserController = TextEditingController(); + final voiceOfServiceUserController = TextEditingController(); + final anyImmediateRisksController = TextEditingController(); + final qActionTakenController = TextEditingController(); + final commentsController = TextEditingController(); + final nameController1 = TextEditingController(); + final nameController2 = TextEditingController(); + final anyWitnessesController = TextEditingController(); + final reportingDateTimeController = TextEditingController(); + final actionTakenController = TextEditingController(); + + final catSubCatController = Get.put(CategorySubcategoryWidgetController()); + + SafeguardingFormScreenController({required CommonCareNoteFormArgs args}) + : super(args: args); + + @override + void onReady() { + catSubCatController.getBodyParts(); + super.onReady(); + } + + @override + void dispose() { + concernAboutServiceUserController.dispose(); + voiceOfServiceUserController.dispose(); + anyImmediateRisksController.dispose(); + qActionTakenController.dispose(); + commentsController.dispose(); + nameController1.dispose(); + nameController2.dispose(); + anyWitnessesController.dispose(); + reportingDateTimeController.dispose(); + actionTakenController.dispose(); + catSubCatController.dispose(); + + Get.delete(); + super.dispose(); + } + + selectDateAndTimeOfReporting(context) async { + Get.focusScope?.unfocus(); + final DateTime? d = await showDialog( + context: context, + builder: (BuildContext context) { + return AdoptiveCalendar( + initialDate: DateTime.now(), + action: true, + ); + }, + ); + // final d = await CommonCode.datePicker(context); + + if (d != null) { + reportingDateTimeController.text = + FrequentFunctions.careNoteDateFormatter.format(d).toLowerCase(); + } + } + + Future onSaveButtonTap() async { + // if (super.date == null || super.time == null) { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + + if (concernAboutServiceUserController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Concerns about the service user field is required", + ); + return; + } + + if (voiceOfServiceUserController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Voice of the service user field is required", + ); + return; + } + + if (anyImmediateRisksController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Are there any immediate risks field is required", + ); + return; + } + + if (catSubCatController.selectedBodyPart() == null) { + FrequentFunctions.showToast( + message: "Please select category first", + ); + return; + } + + if (catSubCatController.selectedBodyPart()!.subCategory.isNotEmpty && + catSubCatController.selectedSubcategory() == null) { + FrequentFunctions.showToast( + message: "Please select subcategory", + ); + return; + } + + super.removeFocus(); + + await onFormSubmit( + request: CreateCarePlanRequest( + isHTML: true, + flag: false, + title: "", + category: catSubCatController.selectedSubcategory()?.id ?? + catSubCatController.selectedBodyPart()!.id, + noteDetails: SafeguardingFormHtmlRequest( + concernsAboutServiceUser: + concernAboutServiceUserController.text.trim(), + voiceOfTheServiceUser: voiceOfServiceUserController.text.trim(), + areThereAnyImmediateRisks: anyImmediateRisksController.text.trim(), + whatActionDoYouFeel: qActionTakenController.text.trim(), + comments: commentsController.text.trim(), + yourName: nameController1.text.trim(), + anyWitnesses: anyWitnessesController.text.trim(), + dateTimeReporting: reportingDateTimeController.text.trim(), + yourNameDslDdsl: nameController2.text.trim(), + actionTaken: actionTakenController.text.trim(), + ).toHtml(), + ), + ); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/showering_bath_form_screen_controller.dart b/lib/controllers/clients/careNoteFormControllers/showering_bath_form_screen_controller.dart new file mode 100644 index 0000000..6effbf4 --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/showering_bath_form_screen_controller.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/careNoteFormsRequests/showering_and_bath_form_html_request.dart'; +import 'package:get/get.dart'; +import 'common_care_note_forms_controller.dart'; + +class ShoweringBathFormScreenController extends CommonCareNoteFormsController { + final commentsController = TextEditingController(); + + final selectedOption = Rx('Bath'); + + ShoweringBathFormScreenController({required CommonCareNoteFormArgs args}) + : super(args: args); + + @override + void dispose() { + commentsController.dispose(); + Get.delete(); + super.dispose(); + } + + Future onSaveButtonTap() async { + // if (super.date == null || super.time == null) { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + + if (commentsController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Comments field is required", + ); + return; + } + + super.removeFocus(); + + await onFormSubmit( + request: CreateCarePlanRequest( + isHTML: true, + flag: false, + title: "", + noteDetails: ShoweringAndBathFormHtmlRequest( + showeringBathType: selectedOption(), + comments: commentsController.text.trim(), + ).toHtml(), + ), + ); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/toileting_note_form_screen_controller.dart b/lib/controllers/clients/careNoteFormControllers/toileting_note_form_screen_controller.dart new file mode 100644 index 0000000..8a08ad9 --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/toileting_note_form_screen_controller.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/careNoteFormsRequests/toileting_form_html_request.dart'; +import 'package:get/get.dart'; +import 'common_care_note_forms_controller.dart'; + +class ToiletingNoteFormScreenController extends CommonCareNoteFormsController { + final commentsController = TextEditingController(); + + final selectedOption = Rx(null); + final assistanceRequired = false.obs; + + ToiletingNoteFormScreenController({required CommonCareNoteFormArgs args}) + : super(args: args); + + @override + void dispose() { + commentsController.dispose(); + Get.delete(); + super.dispose(); + } + + Future onSaveButtonTap() async { + // if (super.date == null || super.time == null) { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + + if (assistanceRequired() && commentsController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Comments field is required", + ); + return; + } + + super.removeFocus(); + + await onFormSubmit( + request: CreateCarePlanRequest( + isHTML: true, + flag: false, + title: "", + noteDetails: ToiletingFormHtmlRequest( + assistanceRequired: assistanceRequired(), + assistanceType: !assistanceRequired() ? "" : (selectedOption() ?? ""), + comments: !assistanceRequired() ? "" : commentsController.text.trim(), + ).toHtml(), + ), + ); + } +} diff --git a/lib/controllers/clients/careNoteFormControllers/weight_height_form_screen_controller.dart b/lib/controllers/clients/careNoteFormControllers/weight_height_form_screen_controller.dart new file mode 100644 index 0000000..c36b4f6 --- /dev/null +++ b/lib/controllers/clients/careNoteFormControllers/weight_height_form_screen_controller.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'common_care_note_forms_controller.dart'; + +class WeightHeightFormScreenController extends CommonCareNoteFormsController { + final heightController = TextEditingController(); + final weightController = TextEditingController(); + final commentsController = TextEditingController(); + + final appointmentWith = [ + "GP", + "CAMHS", + "Psychologist", + "A&E", + "Sexual Health", + "Social Worker", + "Other" + ]; + + WeightHeightFormScreenController({required CommonCareNoteFormArgs args}) + : super(args: args); + + @override + void dispose() { + heightController.dispose(); + weightController.dispose(); + commentsController.dispose(); + Get.delete(); + super.dispose(); + } + + Future onSaveButtonTap() async { + // if (super.date == null || super.time == null) { + if (super.date == null) { + FrequentFunctions.showToast( + message: "Please select date and time first", + ); + return; + } + if (heightController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Height field is required", + ); + return; + } + if (weightController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Weight field is required", + ); + return; + } + if (commentsController.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Comments field is required", + ); + return; + } + + super.removeFocus(); + + await onFormSubmit( + request: CreateCarePlanRequest( + isHTML: true, + flag: false, + title: "", + noteDetails: CreateCarePlanRequest.heightWeightHtmlReq( + heightController.text.trim(), + weightController.text.trim(), + commentsController.text.trim(), + ), + ), + ); + } +} diff --git a/lib/controllers/clients/care_note_detail_screen_controller.dart b/lib/controllers/clients/care_note_detail_screen_controller.dart new file mode 100644 index 0000000..abba24c --- /dev/null +++ b/lib/controllers/clients/care_note_detail_screen_controller.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import '../../models/clients/allCareNotes/CarePlans.dart'; +import '../../view/screens/clients/care_note_detail_screen.dart'; + +class CareNoteDetailScreenController extends GetxController { + final isLoadingWebPage = false.obs; + final webViewHeight = (300.0).obs; + + late final WebViewController webViewController; + + String headerHtml = """
"""; + + final CarePlan data; + + CareNoteDetailScreenController(this.data) { + headerHtml = """

+ ${data.addedby?.name ?? ""} + ${(data.eventDateTime == null) ? "" : DateFormat("dd/MM/yyyy hh:mm aa").format(DateTime.fromMillisecondsSinceEpoch(data.eventDateTime!).toLocal())} +

+ """; + + if (data.isHTML == true) { + final bodyPartHtml = (data.healthIssueId?.category == null) + ? "" + : """

+ Effected Body Part + ${data.healthIssueId!.category!.name} +

"""; + + webViewController = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setBackgroundColor(const Color(0x00000000)) + ..setNavigationDelegate( + NavigationDelegate( + onProgress: (int progress) {}, + onPageStarted: (String url) { + isLoadingWebPage.value = true; + }, + onPageFinished: (String url) { + updateWebViewHeight(); + }, + onHttpError: (HttpResponseError error) {}, + onWebResourceError: (WebResourceError error) {}, + onNavigationRequest: (NavigationRequest request) { + if (request.url.startsWith('https://www.youtube.com/')) { + return NavigationDecision.prevent; + } + return NavigationDecision.navigate; + }, + ), + ) + ..loadHtmlString(CareNoteDetailScreen.meta + + CareNoteDetailScreen.css + + headerHtml + + data.noteDetails! + + bodyPartHtml); + } + } + + void updateWebViewHeight() async { + final height = await webViewController + .runJavaScriptReturningResult('document.body.scrollHeight;'); + if (height is String) { + double h = double.parse(height); + webViewHeight.value = h; + } else if (height is double) { + webViewHeight.value = height; + } + + isLoadingWebPage.value = false; + } +} diff --git a/lib/controllers/clients/care_notes_screen_controller.dart b/lib/controllers/clients/care_notes_screen_controller.dart new file mode 100644 index 0000000..6080459 --- /dev/null +++ b/lib/controllers/clients/care_notes_screen_controller.dart @@ -0,0 +1,273 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/utilities/enums/care_note_form_type.dart'; +import 'package:get/get.dart'; +import '../../ftc_mobile_app.dart'; +import '../../models/clients/care_note_category.dart'; + +class CareNotesScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final List categories = []; + + final categoriesMapList = [ + { + "iconPath": AssetsManager.kIcHealth, + "category": "Health", + "subcategories": [ + { + "iconPath": AssetsManager.kIcInjury, + "name": "Injury / Health Issue", + "formType": CareNotesFormType.injuryHealthIssueForm.text, + "apiValue": CareNotesFormType.injuryHealthIssueForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcWeightHeight, + "name": "Weight / Height", + "formType": CareNotesFormType.weightHeightForm.text, + "apiValue": CareNotesFormType.weightHeightForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcAppointment, + "name": "Health appointments", + "formType": CareNotesFormType.healthAppointmentForm.text, + "apiValue": CareNotesFormType.healthAppointmentForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcSearch, + "name": "Observations", + "formType": CareNotesFormType.observationsForm.text, + "apiValue": CareNotesFormType.observationsForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcOthers, + "name": "Others", + "formType": CareNotesFormType.healthOtherForm.text, + "apiValue": CareNotesFormType.healthOtherForm.apiValue, + } + ] + }, + { + "iconPath": AssetsManager.kIcGeneral, + "category": "General", + "subcategories": [ + { + "iconPath": AssetsManager.kIcNote, + "name": "General Note", + "formType": CareNotesFormType.generalNoteForm.text, + "apiValue": CareNotesFormType.generalNoteForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcActivity, + "name": "Activities", + "formType": CareNotesFormType.activitiesForm.text, + "apiValue": CareNotesFormType.activitiesForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcSleep, + "name": "Sleep", + "formType": CareNotesFormType.sleepForm.text, + "apiValue": CareNotesFormType.sleepForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcSafeguarding, + "name": "Safeguarding", + "formType": CareNotesFormType.safeguardingForm.text, + "apiValue": CareNotesFormType.safeguardingForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcOthers, + "name": "Others", + "formType": CareNotesFormType.generalOtherForm.text, + "apiValue": CareNotesFormType.generalOtherForm.apiValue, + } + ] + }, + { + "iconPath": AssetsManager.kIcPersonalCare, + "category": "Personal Care", + "subcategories": [ + { + "iconPath": AssetsManager.kIcToileting, + "name": "Toileting", + "formType": CareNotesFormType.toiletingNoteForm.text, + "apiValue": CareNotesFormType.toiletingNoteForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcShower, + "name": "Showering / Bath", + "formType": CareNotesFormType.showeringBathForm.text, + "apiValue": CareNotesFormType.showeringBathForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcMouthHygiene, + "name": "Mouth Hygiene", + "formType": CareNotesFormType.mouthHygieneForm.text, + "apiValue": CareNotesFormType.mouthHygieneForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcOthers, + "name": "Others", + "formType": CareNotesFormType.personalCareOtherForm.text, + "apiValue": CareNotesFormType.personalCareOtherForm.apiValue, + } + ] + }, + { + "iconPath": AssetsManager.kIcMentalWellbeing, + "category": "Mental Wellbeing", + "subcategories": [ + { + "iconPath": AssetsManager.kIcMood, + "name": "Mood Rating", + "formType": CareNotesFormType.moodRatingForm.text, + "apiValue": CareNotesFormType.moodRatingForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcAbc, + "name": "ABC", + "formType": CareNotesFormType.ABCForm.text, + "apiValue": CareNotesFormType.ABCForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcPhysicalIntervention, + "name": "Physical Intervention", + "formType": CareNotesFormType.physicalInterventionForm.text, + "apiValue": CareNotesFormType.physicalInterventionForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcConsent, + "name": "Consent, Capacity, MCA & DOLS", + "formType": CareNotesFormType.consentCapacityForm.text, + "apiValue": CareNotesFormType.consentCapacityForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcOthers, + "name": "Others", + "formType": CareNotesFormType.mentalWellbeingOtherForm.text, + "apiValue": CareNotesFormType.mentalWellbeingOtherForm.apiValue, + } + ] + }, + { + "iconPath": AssetsManager.kIcIntractions, + "category": "Professional/family Interactions", + "subcategories": [ + { + "iconPath": AssetsManager.kIcMeeting, + "name": "Meetings", + "formType": CareNotesFormType.meetingsForm.text, + "apiValue": CareNotesFormType.meetingsForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcTelephone, + "name": "Telephone Calls", + "formType": CareNotesFormType.telephoneCallsForm.text, + "apiValue": CareNotesFormType.telephoneCallsForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcReviews, + "name": "Reviews", + "formType": CareNotesFormType.reviewsForm.text, + "apiValue": CareNotesFormType.reviewsForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcEmail, + "name": "Emails", + "formType": CareNotesFormType.emailsForm.text, + "apiValue": CareNotesFormType.emailsForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcOtherInteractions, + "name": "All other interactions", + "formType": CareNotesFormType.allOtherInteractionsForm.text, + "apiValue": CareNotesFormType.allOtherInteractionsForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcOthers, + "name": "Others", + "formType": + CareNotesFormType.professionalFamilyInteractionsOtherForm.text, + "apiValue": + CareNotesFormType.professionalFamilyInteractionsOtherForm.apiValue, + } + ] + }, + { + "iconPath": AssetsManager.kIcIndependentLiving, + "category": "Independent Living", + "subcategories": [ + { + "iconPath": AssetsManager.kIcLaundry, + "name": "Laundry", + "formType": CareNotesFormType.laundryForm.text, + "apiValue": CareNotesFormType.laundryForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcCooking, + "name": "Cooking", + "formType": CareNotesFormType.cookingForm.text, + "apiValue": CareNotesFormType.cookingForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcHydration, + "name": "Nutrition / Hydration", + "formType": CareNotesFormType.nutritionHydrationForm.text, + "apiValue": CareNotesFormType.nutritionHydrationForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcCleaning, + "name": "Cleaning", + "formType": CareNotesFormType.cleaningForm.text, + "apiValue": CareNotesFormType.cleaningForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcFinance, + "name": "Finance", + "formType": CareNotesFormType.financeForm.text, + "apiValue": CareNotesFormType.financeForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcPublicInteraction, + "name": "Public interaction (including transport)", + "formType": CareNotesFormType.publicInteractionForm.text, + "apiValue": CareNotesFormType.publicInteractionForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcEducation, + "name": "Education", + "formType": CareNotesFormType.educationForm.text, + "apiValue": CareNotesFormType.educationForm.apiValue, + }, + { + "iconPath": AssetsManager.kIcOthers, + "name": "Others", + "formType": CareNotesFormType.independentLivingOtherForm.text, + "apiValue": CareNotesFormType.independentLivingOtherForm.apiValue, + } + ] + } + ]; + + @override + void onInit() { + for (var e in categoriesMapList) { + categories.add(CareNoteCategory.fromJson(e)); + } + super.onInit(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + + void backButtonPressed(BuildContext context) { + Get.delete(); + Navigator.of(context).pop(); + } +} diff --git a/lib/controllers/clients/care_notes_subcategories_screen_controller.dart b/lib/controllers/clients/care_notes_subcategories_screen_controller.dart new file mode 100644 index 0000000..4fe2676 --- /dev/null +++ b/lib/controllers/clients/care_notes_subcategories_screen_controller.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/service_users_model.dart'; +import 'package:get/get.dart'; +import '../../ftc_mobile_app.dart'; +import '../../models/clients/care_note_category.dart'; + +class CareNotesSubcategoriesScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + + void backButtonPressed(BuildContext context) { + Get.delete(); + Navigator.of(context).pop(); + } +} diff --git a/lib/controllers/clients/care_plan_menu_screen_controller.dart b/lib/controllers/clients/care_plan_menu_screen_controller.dart new file mode 100644 index 0000000..fedca4d --- /dev/null +++ b/lib/controllers/clients/care_plan_menu_screen_controller.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:get/get.dart'; + +import '../../ftc_mobile_app.dart'; +import '../../models/clients/service_users_model.dart'; + +class CarePlanMenuScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + var argument; + final serviceUser = Rx(null); + + CarePlanMenuScreenController({required UserData data}) { + serviceUser.value = data; + } + + @override + void onInit() { + if (CustomRouteGenerator.argument != null) { + argument = CustomRouteGenerator.argument; + if (argument is ServiceUserModel) { + serviceUser.value = argument; + } + } + super.onInit(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + + void backButtonPressed(BuildContext context) { + Get.delete(); + Navigator.of(context).pop(); + } +} diff --git a/lib/controllers/clients/client_profile_screen_controller.dart b/lib/controllers/clients/client_profile_screen_controller.dart new file mode 100644 index 0000000..b10d898 --- /dev/null +++ b/lib/controllers/clients/client_profile_screen_controller.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/service_users_model.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:get/get.dart'; +import '../../ftc_mobile_app.dart'; + +class ClientProfileScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + final serviceUser = Rx(null); + + ClientProfileScreenController({required UserData data}) { + serviceUser.value = data; + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + + void backButtonPressed(BuildContext context) { + Get.delete(); + Navigator.of(context).pop(); + } +} diff --git a/lib/controllers/clients/clients_list_screen_controller.dart b/lib/controllers/clients/clients_list_screen_controller.dart new file mode 100644 index 0000000..f2078a3 --- /dev/null +++ b/lib/controllers/clients/clients_list_screen_controller.dart @@ -0,0 +1,127 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; +import '../../utilities/frequent_functions.dart'; +import '../../web_services/client_services.dart'; + +class ClientsListScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + final searchController = TextEditingController(); + + final serviceUsersList = RxList(); + final canLoadMore = RxBool(false); + final searchText = "".obs; + + final int _limit = 20; + int _skip = 0; + bool loadingMore = false; + + final _listRC = RefreshController(initialRefresh: false); + + RefreshController get listRC => _listRC; + + final _listSC = ScrollController(); + + ScrollController get listSC => _listSC; + + @override + void onInit() { + debounce(searchText, onSearch, time: 800.milliseconds); + super.onInit(); + } + + @override + void onReady() { + getClients(); + super.onReady(); + } + + void onRefresh() async { + await getClients(); + _listRC.refreshCompleted(); + } + + void onLoading() async { + if (!loadingMore) { + await _loadMore(); + } + + _listRC.loadComplete(); + } + + onSearch(String text) { + getClients(); + } + + Future getClients() async { + serviceUsersList.clear(); + _skip = 0; + var response = await ClientService() + .getServiceUsersList( + searchText: searchText(), + limit: _limit, + offset: _skip, + ) + .showLoader(); + + if (response.success == true) { + if (response.data?.users?.isNotEmpty == true) { + _skip += _limit; + serviceUsersList.value = response.data?.users ?? []; + canLoadMore.value = true; + } else { + canLoadMore.value = searchText.isEmpty; + } + } else { + if (response.message.isNullOrEmptyNot()) { + FrequentFunctions.showToast(message: response.message!); + } + } + } + + Future _loadMore() async { + if (canLoadMore.isTrue) { + loadingMore = true; + + var response = await ClientService().getServiceUsersList( + searchText: searchText(), + limit: _limit, + offset: _skip, + ); + loadingMore = false; + + if (response.success == true) { + if (response.data?.users?.isNotEmpty == true) { + _skip += _limit; + serviceUsersList.addAll(response.data?.users ?? []); + canLoadMore.value = true; + } else { + canLoadMore.value = false; + } + } else { + if (response.message.isNullOrEmptyNot()) { + FrequentFunctions.showToast(message: response.message!); + } + } + } + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + searchController.dispose(); + _listSC.dispose(); + Get.delete(); + super.dispose(); + } + + void backButtonPressed(BuildContext context) { + Get.delete(); + Navigator.of(context).pop(); + } +} diff --git a/lib/controllers/clients/consent_and_capacity_add_new_form_screen_controller.dart b/lib/controllers/clients/consent_and_capacity_add_new_form_screen_controller.dart new file mode 100644 index 0000000..905feaa --- /dev/null +++ b/lib/controllers/clients/consent_and_capacity_add_new_form_screen_controller.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import '../../models/clients/consent_details_model.dart'; + +class ConsentAndCapacityAddNewFormScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + final firstNameController = TextEditingController(); + final lastNameController = TextEditingController(); + final phoneController = TextEditingController(); + final emailController = TextEditingController(); + final genderController = TextEditingController(); + final dobController = TextEditingController(); + final ageController = TextEditingController(); + final descriptionController = TextEditingController(); + + // List dropDownList = ["A","b","C"]; + // RxString selectedDropdownValue = "A".obs; + final isForUpdate = false.obs; + + // String updateConsentId = ""; + // final serviceUser = Rx(null); + ConsentDetailsModel saveResult = ConsentDetailsModel.empty(); + + Future saveButtonPressed(String serviceUserId) async { + var result = await ClientService() + .addConsentDetails( + description: descriptionController.value.text, + staffId: serviceUserId) + .showLoader(); + if (result is ConsentDetailsModel && result.id != "") { + saveResult.description = descriptionController.value.text; + result.description = descriptionController.value.text; + saveResult = result; + FrequentFunctions.showToast(message: "Consent Template added successfully"); + return result; + // return true; + } else { + if (result is String) { + FrequentFunctions.showToast(message: result); + } + return false; + } + } + + Future updateButtonPressed(String consentId) async { + var result = await ClientService() + .updateConsentDetails( + description: descriptionController.value.text, consentId: consentId) + .showLoader(); + if (result is bool && result == true) { + FrequentFunctions.showToast(message: "Consent Template Updated successfully"); + return true; + } else { + if (result is String) { + FrequentFunctions.showToast(message: result); + } + return false; + } + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + firstNameController.dispose(); + lastNameController.dispose(); + phoneController.dispose(); + emailController.dispose(); + genderController.dispose(); + dobController.dispose(); + ageController.dispose(); + descriptionController.dispose(); + Get.delete(); + super.dispose(); + } + + void backButtonPressed(BuildContext context) { + Get.delete(); + Navigator.of(context).pop(); + } +} diff --git a/lib/controllers/clients/consent_and_capacity_questionnaire_screen_controller.dart b/lib/controllers/clients/consent_and_capacity_questionnaire_screen_controller.dart new file mode 100644 index 0000000..1868ac2 --- /dev/null +++ b/lib/controllers/clients/consent_and_capacity_questionnaire_screen_controller.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; + +import '../../models/clients/consent_details_model.dart'; +import '../../models/clients/service_users_model.dart'; + +class ConsentAndCapacityQuestionnaireScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + List dropDownList = ["A", "b", "C"]; + RxString selectedDropdownValue = "A".obs; + RxList consentDetailsList = RxList(); + dynamic argument; + final serviceUser = Rx(null); + + ConsentAndCapacityQuestionnaireScreenController({required UserData data}) { + serviceUser.value = data; + } + + @override + void onReady() { + print("serviceUser() != null ---- ${serviceUser() != null}"); + if (serviceUser() != null) { + fetchList(); + } + super.onReady(); + } + + void fetchList() async { + if (CustomRouteGenerator.argument != null) { + argument = CustomRouteGenerator.argument; + if (argument is ServiceUserModel) { + serviceUser.value = argument; + } + } + var result = await ClientService() + .getConsentDetails(staffId: serviceUser()!.id!) + .showLoader(); + if (result is List) { + consentDetailsList.value = result; + } + } + + void deleteConsentPressed( + {required String consentId, required int index}) async { + var result = + await ClientService().deleteConsent(consentId: consentId).showLoader(); + if (result is bool && result == true) { + FrequentFunctions.showToast(message: "Consent Deleted Successfully"); + consentDetailsList.removeAt(index); + } else { + if (result is String) { + FrequentFunctions.showToast(message: result); + } else { + FrequentFunctions.showToast(message: "Consent Deleted Successfully"); + } + } + } + + Future updateButtonPressed( + String description, String id, int index) async { + var result = await ClientService() + .updateConsentDetails(description: description, consentId: id); + if (result is bool && result == true) { + FrequentFunctions.showToast(message: "Consent Template Updated successfully"); + consentDetailsList[index].description = description; + consentDetailsList.refresh(); + return true; + } else { + if (result is String) { + FrequentFunctions.showToast(message: result); + } + return false; + } + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + + void backButtonPressed(BuildContext context) { + Get.delete(); + Navigator.of(context).pop(); + } +} diff --git a/lib/controllers/clients/current_health_issues_screen_controller.dart b/lib/controllers/clients/current_health_issues_screen_controller.dart new file mode 100644 index 0000000..343e3eb --- /dev/null +++ b/lib/controllers/clients/current_health_issues_screen_controller.dart @@ -0,0 +1,138 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/HealthIssuesDetailsModel.dart'; +import 'package:ftc_mobile_app/models/clients/body_points_category.dart'; +import 'package:ftc_mobile_app/utilities/enums/body_parts.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; + +class BodyPointIssueStyleWrapper { + final HealthIssueDetailsModel data; + Color? color; + + BodyPointIssueStyleWrapper({ + required this.data, + this.color, + }); +} + +class CurrentHealthIssuesScreenController extends GetxController { + static const tabAllIssues = "All Issues"; + static const tabActiveIssues = "Active Issues"; + static const tabResolvedIssues = "Resolved Issues"; + + final GlobalKey screenKey = GlobalKey(); + + RxString selectedDropdownValue = "Active".obs; + + final List tabs = [tabAllIssues, tabActiveIssues, tabResolvedIssues]; + Map oIssueBodyParts = {}; + final issueBodyParts = RxMap(); + + String serviceUserId = ''; + String currentTab = tabAllIssues; + + @override + void onInit() { + super.onInit(); + } + + @override + void onReady() { + getPointsDataFromService(); + super.onReady(); + } + + onTabChange(String tabText) { + switch (tabText) { + case tabAllIssues: + issueBodyParts.value = Map.from(oIssueBodyParts); + break; + case tabActiveIssues: + final f = {}; + f.addEntries(oIssueBodyParts.entries.where((e) => e.value.data.status)); + issueBodyParts.value = f; + break; + case tabResolvedIssues: + final f = {}; + f.addEntries( + oIssueBodyParts.entries.where((e) => e.value.data.status == false)); + issueBodyParts.value = f; + break; + } + + currentTab = tabText; + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + static Color getStatusColor(bool status) { + return status == true ? Colors.red : Colors.green; + } + + /// This method fetches health issues and map their body points to related data and color to show for the point, + void getPointsDataFromService() async { + var result = await ClientService() + .getHealthIssues(userId: serviceUserId) + .showLoader(); + + if (result is List) { + final bodyPartsMap = {}; + const bodyParts = BodyPart.values; + + for (final data in result) { + final bodyPartEnum = bodyParts.firstWhereOrNull( + (e1) => e1.apiValue == data.bodyPointsCategory?.enumed); + if (bodyPartEnum != null) { + bodyPartsMap[bodyPartEnum] = BodyPointIssueStyleWrapper( + data: data, + color: getStatusColor(data.status), + ); + } + } + oIssueBodyParts = bodyPartsMap; + onTabChange(currentTab); + } + } + + Future updateHealthIssueStatus( + {required BodyPart bodyPoint, + required HealthIssueDetailsModel data, + required bool status}) async { + final response = await ClientService() + .updateHealthIssueData( + issueId: data.id, + categoryId: data.bodyPointsCategory!.id, + status: status, + ) + .showLoader(); + + if (response is BodyPointsCategory) { + oIssueBodyParts[bodyPoint]! + ..data.status = status + ..color = getStatusColor(status); + + issueBodyParts[bodyPoint]! + ..data.status = status + ..color = getStatusColor(status); + + Get.back(); + onTabChange(currentTab); + } else if (response is String && response.isNotEmpty) { + FrequentFunctions.showToast(message: response); + } + } + + void onBackPress(BuildContext context) { + Get.delete(); + Navigator.pop(context); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/clients/document_details_screen_controller.dart b/lib/controllers/clients/document_details_screen_controller.dart new file mode 100644 index 0000000..605c915 --- /dev/null +++ b/lib/controllers/clients/document_details_screen_controller.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class DocumentDetailsScreenController extends GetxController{ + + final GlobalKey screenKey = GlobalKey(); + + + void removeFocus(){ + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + +} \ No newline at end of file diff --git a/lib/controllers/clients/documents_list_screen_controller.dart b/lib/controllers/clients/documents_list_screen_controller.dart new file mode 100644 index 0000000..214a172 --- /dev/null +++ b/lib/controllers/clients/documents_list_screen_controller.dart @@ -0,0 +1,113 @@ +import 'dart:io'; +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/documents_list_model.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:path/path.dart'; + +class DocumentsListScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final searchTEC = TextEditingController(); + List oDocumentsList = []; + + final documentsList = RxList(); + final serviceUser = Rx(null); + + DocumentsListScreenController(UserData data) { + serviceUser.value = data; + } + + @override + void onReady() { + getDocuments(); + super.onReady(); + } + + void getDocuments() async { + final response = await ClientService() + .getDocumentListService(serviceUserId: serviceUser()!.id!) + .showLoader(); + if (response is DocumentsListModel) { + if (response.documentList.isNotEmpty) { + response.documentList + .removeWhere((e) => e.title.isEmpty || e.details.isEmpty); + + oDocumentsList = response.documentList; + documentsList.value = response.documentList; + } + } else if (response is String && response.isNotEmpty) { + FrequentFunctions.showToast(message: response); + } + } + + onSearch(String text) { + if (text.isEmpty) { + documentsList.value = oDocumentsList; + } else { + documentsList.value = oDocumentsList + .where((e) => e.title.toLowerCase().contains(text.toLowerCase())) + .toList(); + } + } + + downloadDoc({required String path}) async { + if (path.isNotEmpty) { + final f = await _downloadGooglePlacePhoto(path: path).showLoader(); + + if (f != null) { + FrequentFunctions.showToast( + message: "Document downloaded successfully"); + } + } + } + + Future _downloadGooglePlacePhoto({required String path}) async { + try { + // final hasInternet = await FrequentFunctions.hasInternetConnection; + // debugPrint("hasInternet: $hasInternet"); + // + // if (!hasInternet) { + // return null; + // } + + final tempDir = await getApplicationDocumentsDirectory(); + String fullPath = "${tempDir.path}/${basename(path)}"; + + File file = File(fullPath); + + if (file.existsSync()) { + return null; + } + + var dio = Dio(); + + final response = await dio.get( + "${WebUrls.baseUrl}$path", + options: Options(responseType: ResponseType.bytes), + ); + + if (response.statusCode == 200) { + file.writeAsBytesSync(response.data); + return file; + } + return null; + } on DioException catch (_) { + return null; + } catch (e) { + debugPrint("Web Error: $e"); + return null; + } + } + + @override + void dispose() { + Get.delete(); + searchTEC.dispose(); + super.dispose(); + } +} diff --git a/lib/controllers/clients/export_clients_controllers.dart b/lib/controllers/clients/export_clients_controllers.dart new file mode 100644 index 0000000..930edbc --- /dev/null +++ b/lib/controllers/clients/export_clients_controllers.dart @@ -0,0 +1,20 @@ +export 'clients_list_screen_controller.dart'; +export 'client_profile_screen_controller.dart'; +export 'appointment_screen_controller.dart'; +export 'notes_screen_controller.dart'; +export 'select_note_screen_controller.dart'; +export 'new_note_screen_controller.dart'; +export 'care_plan_menu_screen_controller.dart'; +export 'documents_list_screen_controller.dart'; +export 'document_details_screen_controller.dart'; +export 'recent_incidents_screen_controller.dart'; +export 'current_health_issues_screen_controller.dart'; +export 'consent_and_capacity_add_new_form_screen_controller.dart'; +export 'consent_and_capacity_questionnaire_screen_controller.dart'; +export 'life_history_and_goals_screen_controller.dart'; +export 'pbs_plan_screen_controller.dart'; +export 'photo_gallery_screen_controller.dart'; +export 'risk_assessments_screen_controller.dart'; +export 'risk_assessments_template_screen_controller.dart'; +export 'new_client_module_controllers/export_new_client_module.dart'; +export 'care_notes_screen_controller.dart'; \ No newline at end of file diff --git a/lib/controllers/clients/life_history_and_goals_screen_controller.dart b/lib/controllers/clients/life_history_and_goals_screen_controller.dart new file mode 100644 index 0000000..b0ffbfe --- /dev/null +++ b/lib/controllers/clients/life_history_and_goals_screen_controller.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class LifeHistoryAndGoalsScreenController extends GetxController{ + + final GlobalKey screenKey = GlobalKey(); + TextEditingController lastNameController = TextEditingController(); + List dropDownList = ["A","b","C"]; + RxString selectedDropdownValue = "A".obs; + + void removeFocus(){ + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + + +} \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/add_new_pbs_plan_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/add_new_pbs_plan_screen_controller.dart new file mode 100644 index 0000000..2505571 --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/add_new_pbs_plan_screen_controller.dart @@ -0,0 +1,199 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/PBSPlanModel.dart'; +import 'package:ftc_mobile_app/models/clients/add_pbs_plan_model.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/view/screens/clients/clients_new_view_module/add_new_pbs_plan_screen.dart'; +import 'package:get/get.dart'; + +class AddNewPbsPlanScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + // dynamic argument; + + // final serviceUser = Rx(null); + + // String userId = ""; + final addPbsPlanModel = PbsList.empty().obs; + final enableSubmitButton = true.obs; + + late final AddNewPBSPlanScreenArgs args; + + AddNewPbsPlanScreenController(AddNewPBSPlanScreenArgs data) { + args = data; + if (data.pbsData != null) { + addPbsPlanModel.value = data.pbsData!; + } + } + + @override + void onInit() { + // if (CustomRouteGenerator.argument != null) { + // argument = CustomRouteGenerator.argument; + // if (argument is ServiceUserModel) { + // serviceUser.value = argument; + // } else if (argument is List) { + // if (argument[1] is ServiceUserModel) { + // ServiceUserModel user = argument[1]; + // userId = user.id; + // } + // if (argument[0] is PbsList) { + // PbsList listItem = argument[0]; + // addPbsPlanModel.value.userId = listItem.userIdModelInPbs?.id ?? ""; + // addPbsPlanModel.value.staffId = listItem.staffId?.id ?? ""; + // addPbsPlanModel.value.planId = listItem.id; + // addPbsPlanModel.value.aboutPlanNote = listItem.aboutPlan; + // addPbsPlanModel.value.managementOfBehaviouralPresentationNote = + // listItem.managementOfBehaviorPlan; + // addPbsPlanModel.value.secondaryPreventionNote = + // listItem.secondaryPrevention; + // addPbsPlanModel.value.reactiveStrategiesNote = + // listItem.reactiveStrategies; + // addPbsPlanModel.value.postIncidentSupportRecoveryNote = + // listItem.postIncidentSupport; + // } + // } + // } + super.onInit(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + // void enableDisableSubmitButton() async { + // enableSubmitButton.value = await addPbsPlanModel.value.areAllFieldsEdited; + // } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + + void backButtonPressed(BuildContext context, dynamic argument) { + addPbsPlanModel.value.postIncidentSupportRecoveryQuillController.dispose(); + addPbsPlanModel.value.reactiveStrategiesQuillController.dispose(); + addPbsPlanModel.value.secondaryPreventionQuillController.dispose(); + addPbsPlanModel.value.managementOfBehaviouralPresentationQuillController + .dispose(); + addPbsPlanModel.value.aboutPlanQuillController.dispose(); + Get.delete(); + Navigator.of(context).pop(argument); + } + + void submitButtonPressed() async { + //post Incident Support + final postIncidentSupportText = await addPbsPlanModel + .value.postIncidentSupportRecoveryQuillController + .getText(); + + if (postIncidentSupportText.isEmpty) { + FrequentFunctions.showToast(message: "All fields are mandatory."); + return; + } + + //reactive Strategies + final reactiveStrategiesText = + await addPbsPlanModel.value.reactiveStrategiesQuillController.getText(); + + if (reactiveStrategiesText.isEmpty) { + FrequentFunctions.showToast(message: "All fields are mandatory."); + return; + } + + //secondaryPrevention + final secondaryPreventionText = await addPbsPlanModel + .value.secondaryPreventionQuillController + .getText(); + + if (secondaryPreventionText.isEmpty) { + FrequentFunctions.showToast(message: "All fields are mandatory."); + return; + } + + //management Of Behavior Plan + final managementOfBehaviorPlanText = await addPbsPlanModel + .value.managementOfBehaviouralPresentationQuillController + .getText(); + + if (managementOfBehaviorPlanText.isEmpty) { + FrequentFunctions.showToast(message: "All fields are mandatory."); + return; + } + + //about Plan + final aboutPlanText = + await addPbsPlanModel.value.aboutPlanQuillController.getText(); + + if (aboutPlanText.isEmpty) { + FrequentFunctions.showToast(message: "All fields are mandatory."); + return; + } + + dynamic response; + + if (args.pbsData == null) { + response = await ClientService() + .addPbsPlanService( + userId: args.userData.id!, + staffId: LocalStorageManager.userId, + postIncidentSupport: postIncidentSupportText, + reactiveStartegies: reactiveStrategiesText, + secondaryPrevention: secondaryPreventionText, + managementOfBehaviorPlan: managementOfBehaviorPlanText, + aboutPlan: aboutPlanText) + .showLoader(); + } else { + response = await ClientService() + .updatePbsPlanService( + id: addPbsPlanModel().id, + userId: args.userData.id!, + staffId: LocalStorageManager.userId, + postIncidentSupport: postIncidentSupportText, + reactiveStartegies: reactiveStrategiesText, + secondaryPrevention: secondaryPreventionText, + managementOfBehaviorPlan: managementOfBehaviorPlanText, + aboutPlan: aboutPlanText, + ) + .showLoader(); + } + + if (response is AddPBSPlanModel) { + print("-------------response is AddPBSPlanModel-----------"); + print({response.toString()}); + print("------------------------"); + backButtonPressed(screenKey.currentContext!, true); + } else { + FrequentFunctions.showToast(message: response.toString()); + } + } + +// void updateButtonPressed() async { +// dynamic response = await ClientService() +// .updatePbsPlanService( +// id: addPbsPlanModel().id, +// userId: args.userData.id!, +// staffId: FrequentFunctions.userModel.value.id, +// postIncidentSupport: await addPbsPlanModel +// .value.postIncidentSupportRecoveryQuillController +// .getText(), +// reactiveStartegies: await addPbsPlanModel +// .value.reactiveStrategiesQuillController +// .getText(), +// secondaryPrevention: await addPbsPlanModel +// .value.secondaryPreventionQuillController +// .getText(), +// managementOfBehaviorPlan: await addPbsPlanModel +// .value.managementOfBehaviouralPresentationQuillController +// .getText(), +// aboutPlan: +// await addPbsPlanModel.value.aboutPlanQuillController.getText()) +// .showLoader(); +// if (response is AddPBSPlanModel) { +// backButtonPressed(screenKey.currentContext!, true); +// } else { +// FrequentFunctions.showToast(message: response.toString()); +// } +// } +} diff --git a/lib/controllers/clients/new_client_module_controllers/crisis_management_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/crisis_management_screen_controller.dart new file mode 100644 index 0000000..f292773 --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/crisis_management_screen_controller.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class CrisisManagementScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + +} \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/export_new_client_module.dart b/lib/controllers/clients/new_client_module_controllers/export_new_client_module.dart new file mode 100644 index 0000000..728eade --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/export_new_client_module.dart @@ -0,0 +1,12 @@ +export 'crisis_management_screen_controller.dart'; +export 'future_plans_screen_controller.dart'; +export 'health_full_body_map_screen_controller.dart'; +export 'health_screen_controller.dart'; +export 'medication_screen_controller.dart'; +export 'introduction_screen_controller.dart'; +export 'mental_health_screen_controller.dart'; +export 'my_current_plan_screen_controller.dart'; +export 'my_interests_screen_controller.dart'; +export 'overview_screen_controller.dart'; +export 'things_i_want_you_to_help_me_screen_controller.dart'; +export 'support_plan_screen_controller.dart'; \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/future_plans_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/future_plans_screen_controller.dart new file mode 100644 index 0000000..ba3c571 --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/future_plans_screen_controller.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class FuturePlansScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + +} \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/health_full_body_map_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/health_full_body_map_screen_controller.dart new file mode 100644 index 0000000..aeef97e --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/health_full_body_map_screen_controller.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class HealthFullBodyMapScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + List activeList = ["Active","Expired"]; + RxString selectedDropdownValue = "Active".obs; + RxBool firstPointVisible = false.obs; + RxString firstPointSelectedDropdownValue = "Active".obs; + RxBool secondPointVisible = false.obs; + RxString secondPointSelectedDropdownValue = "Expired".obs; + RxBool thirdPointVisible = false.obs; + RxString thirdPointSelectedDropdownValue = "Active".obs; + RxBool fourthPointVisible = false.obs; + RxString fourthPointSelectedDropdownValue = "Expired".obs; + RxBool fifthPointVisible = false.obs; + RxString fifthPointSelectedDropdownValue = "Active".obs; + RxBool sixthPointVisible = false.obs; + RxString sixthPointSelectedDropdownValue = "Expired".obs; + + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + + void backButtonPressed(BuildContext context) { + Get.delete(); + Navigator.of(context).pop(); + } +} \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/health_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/health_screen_controller.dart new file mode 100644 index 0000000..6b7c64f --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/health_screen_controller.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class HealthScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + +} \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/introduction_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/introduction_screen_controller.dart new file mode 100644 index 0000000..29dc826 --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/introduction_screen_controller.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class ClientsIntroductionScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + +} \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/medication_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/medication_screen_controller.dart new file mode 100644 index 0000000..c432c0a --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/medication_screen_controller.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class MedicationScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + void backButtonPressed(BuildContext context) { + Get.delete(); + Navigator.of(context).pop(); + } + +} \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/mental_health_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/mental_health_screen_controller.dart new file mode 100644 index 0000000..345baed --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/mental_health_screen_controller.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class MentalHealthScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + +} \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/my_current_plan_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/my_current_plan_screen_controller.dart new file mode 100644 index 0000000..a82d5a8 --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/my_current_plan_screen_controller.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class MyCurrentPlanScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + +} \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/my_interests_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/my_interests_screen_controller.dart new file mode 100644 index 0000000..4658a50 --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/my_interests_screen_controller.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class MyInterestsScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + +} \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/overview_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/overview_screen_controller.dart new file mode 100644 index 0000000..ffe20af --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/overview_screen_controller.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class OverViewScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + +} \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/support_plan_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/support_plan_screen_controller.dart new file mode 100644 index 0000000..2e42856 --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/support_plan_screen_controller.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class SupportPlanScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + +} \ No newline at end of file diff --git a/lib/controllers/clients/new_client_module_controllers/things_i_want_you_to_help_me_screen_controller.dart b/lib/controllers/clients/new_client_module_controllers/things_i_want_you_to_help_me_screen_controller.dart new file mode 100644 index 0000000..83eb0fa --- /dev/null +++ b/lib/controllers/clients/new_client_module_controllers/things_i_want_you_to_help_me_screen_controller.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class ThingsIWantYouToHelpMeWithScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } + +} \ No newline at end of file diff --git a/lib/controllers/clients/new_note_screen_controller.dart b/lib/controllers/clients/new_note_screen_controller.dart new file mode 100644 index 0000000..afd433c --- /dev/null +++ b/lib/controllers/clients/new_note_screen_controller.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../ftc_mobile_app.dart'; + +class NewNoteScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + final TextEditingController titleController = TextEditingController(); + + final UserModel user = UserModel( + name: 'John Doe', + profilePicture: 'assets/profile_picture.jpg', // Replace with the actual path or URL + phoneNumber: '123-456-7890', + homeAddress: '123 Main St, City ville', + nextOfKin: 'Jane Doe', + diagnosisDate: "Dec. 19", + diagnosisHistory: "A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here.", + aboutPatient: 'A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here.', + ); + List users = ["Hailey Johnson","Ryan Porter","Alan Cruz","Jiwon Nguyen","Patrick Lewis","Isabella Garcia","Yin Chiew","Rebecca Nicholson"]; + TextEditingController searchController = TextEditingController(); + + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + titleController.dispose(); + Get.delete(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/controllers/clients/notes_screen_controller.dart b/lib/controllers/clients/notes_screen_controller.dart new file mode 100644 index 0000000..0952b8d --- /dev/null +++ b/lib/controllers/clients/notes_screen_controller.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../ftc_mobile_app.dart'; + +class NotesScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + + final UserModel user = UserModel( + name: 'John Doe', + profilePicture: 'assets/profile_picture.jpg', // Replace with the actual path or URL + phoneNumber: '123-456-7890', + homeAddress: '123 Main St, City ville', + nextOfKin: 'Jane Doe', + diagnosisDate: "Dec. 19", + diagnosisHistory: "A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here.", + aboutPatient: 'A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here.', + ); + List users = ["Hailey Johnson","Ryan Porter","Alan Cruz","Jiwon Nguyen","Patrick Lewis","Isabella Garcia","Yin Chiew","Rebecca Nicholson"]; + TextEditingController searchController = TextEditingController(); + + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/controllers/clients/pbs_plan_screen_controller.dart b/lib/controllers/clients/pbs_plan_screen_controller.dart new file mode 100644 index 0000000..46e8956 --- /dev/null +++ b/lib/controllers/clients/pbs_plan_screen_controller.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/PBSPlanModel.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; + +class PBSPlanScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final searchTEC = TextEditingController(); + + // dynamic argument; + final serviceUser = Rx(null); + final pbsList = RxList(); + + @override + void onReady() { + fetchPBSPlanList(); + super.onReady(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + Future fetchPBSPlanList() async { + dynamic result = await ClientService() + .getPbsPlanListService(userId: serviceUser.value!.id!) + .showLoader(); + if (result is PBSListDataJson) { + if (result.pbsList.isNotEmpty) { + pbsList.value = result.pbsList.take(1).toList(); + } + } + } + + void onBackPress(BuildContext context) { + Get.delete(); + Navigator.pop(context); + } + + @override + void dispose() { + searchTEC.dispose(); + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/clients/photo_gallery_screen_controller.dart b/lib/controllers/clients/photo_gallery_screen_controller.dart new file mode 100644 index 0000000..514bd21 --- /dev/null +++ b/lib/controllers/clients/photo_gallery_screen_controller.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/memoryListResponse/MemoryListData.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/utilities/frequent_functions.dart'; +import 'package:ftc_mobile_app/web_services/client_services.dart'; +import 'package:get/get.dart'; + +class PhotoGalleryScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + late final String serviceUserId; + + final memoryList = RxList(); + + @override + void onReady() { + getMemoryList(); + super.onReady(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + Future getMemoryList() async { + // serviceUsersList.clear(); + // _skip = 0; + var response = await ClientService() + .getMemoryList(serviceUserId: serviceUserId + // limit: _limit, + // offset: _skip, + ) + .showLoader(); + + if (response.success == true) { + if (response.data?.list?.isNotEmpty == true) { + // _skip += _limit; + memoryList.value = response.data?.list ?? []; + // canLoadMore.value = true; + } else { + // canLoadMore.value = searchText.isEmpty; + } + } else { + if (response.message.isNullOrEmptyNot()) { + FrequentFunctions.showToast(message: response.message!); + } + } + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/clients/recent_incidents_screen_controller.dart b/lib/controllers/clients/recent_incidents_screen_controller.dart new file mode 100644 index 0000000..ca466ee --- /dev/null +++ b/lib/controllers/clients/recent_incidents_screen_controller.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import '../../models/clients/recent_incidents_model.dart'; + +class RecentIncidentsScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final searchTEC = TextEditingController(); + + List oRecentIncidentsList = []; + + RxList recentIncidentsList = RxList(); + final serviceUser = Rx(null); + + RecentIncidentsScreenController(UserData data) { + serviceUser(data); + } + + @override + void onReady() { + fetchRecentIncidentList(); + super.onReady(); + } + + void fetchRecentIncidentList() async { + if (serviceUser() == null) { + return; + } + + var result = await ClientService() + .getRecentIncidentsListService(userId: serviceUser()!.id!) + .showLoader(); + if (result is List) { + oRecentIncidentsList = List.from(result); + recentIncidentsList.value = result; + } else { + FrequentFunctions.showToast(message: result.toString()); + } + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + onSearch(String text) { + if (text.isEmpty) { + recentIncidentsList.value = oRecentIncidentsList; + } else { + recentIncidentsList.value = oRecentIncidentsList + .where( + (e) => e.incidentTitle.toLowerCase().contains(text.toLowerCase())) + .toList(); + } + } + + @override + void dispose() { + searchTEC.dispose(); + Get.delete(); + super.dispose(); + } + + void onBackPress(BuildContext context) { + Get.delete(); + Navigator.pop(context); + } +} diff --git a/lib/controllers/clients/risk_assessments_screen_controller.dart b/lib/controllers/clients/risk_assessments_screen_controller.dart new file mode 100644 index 0000000..571f37e --- /dev/null +++ b/lib/controllers/clients/risk_assessments_screen_controller.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class RiskAssessmentsScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + + void removeFocus(){ + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/controllers/clients/risk_assessments_template_screen_controller.dart b/lib/controllers/clients/risk_assessments_template_screen_controller.dart new file mode 100644 index 0000000..3654284 --- /dev/null +++ b/lib/controllers/clients/risk_assessments_template_screen_controller.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/riskAssessmentResponse/RiskAssessmentData.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/utilities/frequent_functions.dart'; +import 'package:ftc_mobile_app/web_services/client_services.dart'; +import 'package:get/get.dart'; + +class RiskAssessmentsTemplateScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + late final String serviceUserId; + final list = RxList(); + + RiskAssessmentsTemplateScreenController(String userId) { + serviceUserId = userId; + } + + @override + void onReady() { + getRiskAssessments(); + super.onReady(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + void getRiskAssessments() async { + var response = await ClientService() + .getRiskAssessments(serviceUserId: serviceUserId) + .showLoader(); + + if (response.success == true) { + list.value = response.data ?? []; + } else { + if (response.message.isNotNullOrEmpty()) { + FrequentFunctions.showToast(message: response.message!); + } + } + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/clients/select_note_screen_controller.dart b/lib/controllers/clients/select_note_screen_controller.dart new file mode 100644 index 0000000..d42460c --- /dev/null +++ b/lib/controllers/clients/select_note_screen_controller.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../ftc_mobile_app.dart'; + +class SelectNoteScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + final UserModel user = UserModel( + name: 'John Doe', + profilePicture: 'assets/profile_picture.jpg', // Replace with the actual path or URL + phoneNumber: '123-456-7890', + homeAddress: '123 Main St, City ville', + nextOfKin: 'Jane Doe', + diagnosisDate: "Dec. 19", + diagnosisHistory: "A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here.", + aboutPatient: 'A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here. A quick preview of the diagnosis will be shown here.', + ); + List users = ["Hailey Johnson","Ryan Porter","Alan Cruz","Jiwon Nguyen","Patrick Lewis","Isabella Garcia","Yin Chiew","Rebecca Nicholson"]; + TextEditingController searchController = TextEditingController(); + + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/controllers/custom_navigation_drawer_controller.dart b/lib/controllers/custom_navigation_drawer_controller.dart new file mode 100644 index 0000000..45ccc93 --- /dev/null +++ b/lib/controllers/custom_navigation_drawer_controller.dart @@ -0,0 +1,8 @@ + +import 'package:get/get.dart'; + +class CustomNavigationDrawerController extends GetxController{ + RxInt selectedIndex = 1.obs; + + +} \ No newline at end of file diff --git a/lib/controllers/export_controllers.dart b/lib/controllers/export_controllers.dart new file mode 100644 index 0000000..b6d2f14 --- /dev/null +++ b/lib/controllers/export_controllers.dart @@ -0,0 +1,7 @@ +export 'auth_module/export_auth_module.dart'; +export 'rota/export_rota_controller.dart'; +export 'clients/export_clients_controllers.dart'; +export 'home/export_home_controllers.dart'; +export 'notifications/export_notifications_controllers.dart'; +export 'profile/export_profile_controllers.dart'; +export 'custom_navigation_drawer_controller.dart'; \ No newline at end of file diff --git a/lib/controllers/home/dashboard_screen_controller.dart b/lib/controllers/home/dashboard_screen_controller.dart new file mode 100644 index 0000000..b3d4dc1 --- /dev/null +++ b/lib/controllers/home/dashboard_screen_controller.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/profile_screen_model.dart'; +import 'package:ftc_mobile_app/models/rota/LiveRoasterResponseData.dart'; +import 'package:ftc_mobile_app/models/rota/WeekArrayData.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import '../../ftc_mobile_app.dart'; + +class DashboardScreenController extends GetxController { + static DashboardScreenController get instance => + Get.find(); + + final GlobalKey screenKey = GlobalKey(); + + final selectedIndex = 1.obs; + final myShiftsList = RxList(); + final ongoingShift = Rx(null); + final myProfileData = Rx(null); + + @override + void onInit() { + _initOngoingShift(); + super.onInit(); + } + + @override + void onReady() { + getProfileDetail(); + getMyShifts(); + super.onReady(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + _initOngoingShift() { + final shift = LocalStorageManager.getOngoingShift(); + + if (shift != null) { + //meaning shift expired already + if (shift.endTime != null && shift.endTime!.isBefore(TimeOfDay.now())) { + LocalStorageManager.removeOngoingShift(); + } else { + ongoingShift.value = shift; + } + } + } + + getProfileDetail() async { + final resp = await ClientService().getUserDetails(); + + if (resp is ProfileDataModel) { + myProfileData.value = resp.data?.staffMembers?.firstOrNull; + } + } + + Future getMyShifts() async { + var response = await RotaService().getMyShifts( + startDateMills: DateTime.now().subtract(30.days).millisecondsSinceEpoch, + endDateMills: DateTime.now().add(30.days).millisecondsSinceEpoch); + if (response is LiveRoasterResponseData) { + response.daysArray = (response.daysArray.isNullOrEmpty()) + ? [] + : (response.daysArray! + ..sort((a, b) => a.shiftDate!.compareTo(b.shiftDate!))); + + List todayAndFutureShifts = []; + + //Extracting today's and future shifts + for (final shift in response.daysArray!) { + if (shift.shiftDateTime == null || + shift.startTime == null || + shift.endTime == null) { + debugPrint( + "shiftId: ${shift.id} has missing shiftDateTime or startTime or endTime. Please check"); + return; + } + + //Note: matching only date, month, year + final isTodayShift = + DateFormatter.dateFormatter.format(shift.shiftDateTime!) == + DateFormatter.dateFormatter.format(DateTime.now()); + + final isFutureShift = shift.shiftDateTime!.isAfter(DateTime.now()); + + if (isTodayShift) { + //checking and adding today's not expired shift + + // shift not expired yet + if (shift.endTime!.isAfter(TimeOfDay.now())) { + todayAndFutureShifts.add(shift); + + if (ongoingShift() == null) { + LocalStorageManager.saveShiftData(data: shift); + ongoingShift.value = shift; + } + } + } else if (isFutureShift) { + todayAndFutureShifts.add(shift); + } + } + + myShiftsList.value = todayAndFutureShifts.take(2).toList(); + } else if (response is String && response.isNotEmpty) { + FrequentFunctions.showToast(message: response); + } + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/home/export_home_controllers.dart b/lib/controllers/home/export_home_controllers.dart new file mode 100644 index 0000000..b6c266d --- /dev/null +++ b/lib/controllers/home/export_home_controllers.dart @@ -0,0 +1,3 @@ +export 'dashboard_screen_controller.dart'; +export '../../view/screens/chat/controller/chat_screen_controller.dart'; +export 'inbox_screen_controller.dart'; \ No newline at end of file diff --git a/lib/controllers/home/inbox_screen_controller.dart b/lib/controllers/home/inbox_screen_controller.dart new file mode 100644 index 0000000..3657a46 --- /dev/null +++ b/lib/controllers/home/inbox_screen_controller.dart @@ -0,0 +1,156 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/chat/combined_last_messages_model_class.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/view/screens/chat/arguments/group_data_args.dart'; +import 'package:get/get.dart'; +import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; +import '../../ftc_mobile_app.dart'; +import '../../web_services/chat_services.dart'; + +class InboxScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + // CombinedMessageModel combinedMessageModel = CombinedMessageModel.empty(); + final String privacyPolicy = + "A quick preview of the text will be shown here. A quick preview of the text will be shown here. shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here.\n shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here.\n shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here."; + + final checkBoxValue = false.obs; + final chatsAndGroups = RxList(); + + // final sortedChatsAndGroups = RxList(); + + final canLoadMore = RxBool(false); + final onFirstMessageSend = false.obs; + + // late IO.Socket socket; + // final int _limit = 20; + // int _skip = 0; + bool loadingMore = false; + + final _listRC = RefreshController(initialRefresh: false); + + RefreshController get listRC => _listRC; + + final _listSC = ScrollController(); + + ScrollController get listSC => _listSC; + String myId = ""; + + @override + void onInit() { + //Getting my ID + // String userJson = LocalStorageManager.getSessionToken( + // tokenKey: LocalStorageKeys.kUserModelKey, + // ); + // UserModel userModel = UserModel.fromJson(json.decode(userJson)); + myId = LocalStorageManager.userId; + + onFirstMessageSend.listen((isTrue) { + if (isTrue) { + onFirstMessageSend(false); + getChatsAndGroupsList(); + } + }); + super.onInit(); + } + + @override + void onReady() { + // connect(); + getChatsAndGroupsList(); + super.onReady(); + } + + void onBackButtonPressed() { + Get.delete(); + Navigator.pop(screenKey.currentState!.context); + } + + // void showPrivacyDialog() { + // showDialog( + // context: screenKey.currentState!.context, + // builder: (BuildContext context) { + // return PrivacyPolicyDialog( + // privacyPolicy: privacyPolicy, + // checkBoxOnChange: (value) { + // checkBoxValue.value = value; + // }, + // ); + // }, + // ); + // } + + void onRefresh() async { + await getChatsAndGroupsList(); + _listRC.refreshCompleted(); + } + + void onLoading() async { + // if (!loadingMore) { + // await _loadMore(); + // } + + _listRC.loadComplete(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + Future getChatsAndGroupsList() async { + final resp = + await ChatService().combinedLastMessage(userId: myId).showLoader(); + + if (resp is CombinedMessageModel) { + List messages = []; + //Private Messages transform + for (final e in resp.personalMessage) { + messages.add( + MessagesListModel( + otherUserId: (e.senderId == myId) ? e.recieverId : e.senderId, + image: e.image, + title: e.name, + previewOfLastMessage: e.message, + messageType: e.messageType, + date: DateTime.tryParse(e.date)?.millisecondsSinceEpoch ?? + DateTime.now().millisecondsSinceEpoch, + // messageDateTime: FrequentFunctions.toTimesAgo(e.date), + // personalMessageIndex: index, + ), + ); + } + + //Group Messages transform + for (final e in resp.sortedArrayGroup) { + messages.add( + MessagesListModel( + image: e.groupImage, + title: e.groupName, + previewOfLastMessage: e.lastMessages.message, + messageType: MessageType.message.name, + // messageType: e.lastMessages.messageType ?? MessageType.message.name, + date: DateTime.tryParse(e.date)?.millisecondsSinceEpoch ?? + DateTime.now().millisecondsSinceEpoch, + // messageDateTime: FrequentFunctions.toTimesAgo(e.date), + isGroup: true, + groupData: GroupDataArgs( + groupId: e.id, + groupMembersIds: e.groupMembers, + scheduleTime: e.groupWorkingScheduleTime, + ), + ), + ); + } + + chatsAndGroups.value = messages; + } else { + FrequentFunctions.showToast(message: resp["message"]); + } + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/home/select_user_for_chat_screen_controller.dart b/lib/controllers/home/select_user_for_chat_screen_controller.dart new file mode 100644 index 0000000..8ceaf5a --- /dev/null +++ b/lib/controllers/home/select_user_for_chat_screen_controller.dart @@ -0,0 +1,146 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/utilities/local_storage_manager/local_storage_manager.dart'; +import 'package:get/get.dart'; +import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; +import '../../utilities/frequent_functions.dart'; +import '../../web_services/client_services.dart'; + +class SelectUserForChatScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + final searchController = TextEditingController(); + + List oStaffUsersList = []; + + final staffUsersList = RxList(); + final canLoadMore = RxBool(false); + final searchText = "".obs; + + final int _limit = 20; + int _skip = 0; + bool loadingMore = false; + + final _listRC = RefreshController(initialRefresh: false); + + RefreshController get listRC => _listRC; + + final _listSC = ScrollController(); + + ScrollController get listSC => _listSC; + + @override + void onInit() { + // debounce(searchText, onSearch, time: 800.milliseconds); + super.onInit(); + } + + @override + void onReady() { + getStaffMembers(); + super.onReady(); + } + + void onRefresh() async { + await getStaffMembers(); + _listRC.refreshCompleted(); + } + + void onLoading() async { + if (!loadingMore) { + await _loadMore(); + } + + _listRC.loadComplete(); + } + + onSearch(String text) { + // getStaffMembers(); + + if (text.isEmpty) { + staffUsersList.value = List.from(oStaffUsersList); + } else { + staffUsersList.value = oStaffUsersList + .where( + (e) => e.displayName.toLowerCase().contains(text.toLowerCase())) + .toList(); + + // Future.forEach(oStaffUsersList, (e) => null); + } + } + + Future getStaffMembers() async { + staffUsersList.clear(); + _skip = 0; + var response = await (ClientService() + .getAllUsersList( + searchText: searchText(), + limit: _limit, + offset: _skip, + ) + .showLoader()); + + if (response.success == true) { + if (response.data?.users?.isNotEmpty == true) { + _skip += _limit; + + final list = response.data!.users! + ..removeWhere((e) => e.id == LocalStorageManager.userId); + + oStaffUsersList = List.from(list); + staffUsersList.value = list; + // canLoadMore.value = false; + } else { + // canLoadMore.value = searchText.isEmpty; + } + } else { + if (response.message.isNullOrEmptyNot()) { + FrequentFunctions.showToast(message: response.message!); + } + } + } + + Future _loadMore() async { + if (canLoadMore.isTrue) { + loadingMore = true; + + var response = await ClientService().getServiceUsersList( + searchText: searchText(), + limit: _limit, + offset: _skip, + ); + loadingMore = false; + + if (response.success == true) { + if (response.data?.users?.isNotEmpty == true) { + _skip += _limit; + staffUsersList.addAll(response.data?.users ?? []); + canLoadMore.value = true; + } else { + canLoadMore.value = false; + } + } else { + if (response.message.isNullOrEmptyNot()) { + FrequentFunctions.showToast(message: response.message!); + } + } + } + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + searchController.dispose(); + _listSC.dispose(); + Get.delete(); + super.dispose(); + } + + void backButtonPressed(BuildContext context) { + Get.delete(); + Navigator.of(context).pop(); + } +} diff --git a/lib/controllers/notifications/export_notifications_controllers.dart b/lib/controllers/notifications/export_notifications_controllers.dart new file mode 100644 index 0000000..71a1588 --- /dev/null +++ b/lib/controllers/notifications/export_notifications_controllers.dart @@ -0,0 +1 @@ +export "notifications_list_screen_controller.dart"; \ No newline at end of file diff --git a/lib/controllers/notifications/notifications_list_screen_controller.dart b/lib/controllers/notifications/notifications_list_screen_controller.dart new file mode 100644 index 0000000..147b16f --- /dev/null +++ b/lib/controllers/notifications/notifications_list_screen_controller.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/utilities/frequent_functions.dart'; +import 'package:ftc_mobile_app/web_services/notification_services.dart'; +import 'package:get/get.dart'; + +class NotificationListScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + RxBool privacyPolicyAccepted = false.obs; + final String privacyPolicy = + "A quick preview of the text will be shown here. A quick preview of the text will be shown here. shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here.\n shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here.\n shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here."; + + // RxInt selectedIndex = 4.obs; + + @override + void onReady() { + getNotifications(); + super.onReady(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + Future getNotifications() async { + // serviceUsersList.clear(); + // _skip = 0; + var response = await NotificationService().getNotifications().showLoader(); + + if (response.success == true) { + // if (response.data?.users?.isNotEmpty == true) { + // _skip += _limit; + // serviceUsersList.value = response.data?.users ?? []; + // canLoadMore.value = true; + // } else { + // canLoadMore.value = searchText.isEmpty; + // } + } else { + if (response.message.isNullOrEmptyNot()) { + FrequentFunctions.showToast(message: response.message!); + } + } + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/profile/export_profile_controllers.dart b/lib/controllers/profile/export_profile_controllers.dart new file mode 100644 index 0000000..be26eaa --- /dev/null +++ b/lib/controllers/profile/export_profile_controllers.dart @@ -0,0 +1 @@ +export 'view_profile_screen_controller.dart'; \ No newline at end of file diff --git a/lib/controllers/profile/view_profile_screen_controller.dart b/lib/controllers/profile/view_profile_screen_controller.dart new file mode 100644 index 0000000..e4b6b46 --- /dev/null +++ b/lib/controllers/profile/view_profile_screen_controller.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/dialogs/app_dialogs.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; +import '../../models/profile_screen_model.dart'; + +class ViewProfileScreenController extends GetxController { + final nameTEC = TextEditingController(); + final emailTEC = TextEditingController(); + final phoneTEC = TextEditingController(); + final addressTEC = TextEditingController(); + + final GlobalKey screenKey = GlobalKey(); + final dashboardController = DashboardScreenController.instance; + + final viewProfileClient = false.obs; + final covidCheck = false.obs; + final isEditable = false.obs; + + final detail = Rx(null); + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void onInit() { + // FrequentFunctions.profileDataModelNew = ProfileDataModel.fromJson( + // json.decode(LocalStorageManager.getSessionToken( + // tokenKey: LocalStorageKeys.kProfileModelKey))); + + detail.listen((d) { + nameTEC.text = d?.user?.displayName ?? ""; + emailTEC.text = d?.user!.email ?? ""; + phoneTEC.text = d?.user?.phoneNumber ?? ""; + addressTEC.text = d?.user?.modelId?.homeAddress ?? ""; + }); + + detail.value = dashboardController.myProfileData(); + super.onInit(); + } + + @override + onReady() { + // getProfileDetail(); + super.onReady(); + } + + // getProfileDetail() async { + // final resp = await ClientService().getUserDetails().showLoader(); + // + // if (resp is ProfileDataModel) { + // detail.value = resp.data?.staffMembers?.firstOrNull; + // covidCheck.value = + // resp.data?.staffMembers?.firstOrNull?.covidCheck ?? false; + // } else { + // if (resp.isNotNullOrEmpty()) { + // FrequentFunctions.showToast(message: resp); + // } + // } + // } + + void logoutPressed() { + AppDialog.alertAndLogout(() { + FrequentFunctions().logoutButtonPressed(screenKey.currentState!.context); + }); + // showDialog( + // context: screenKey.currentState!.context, + // builder: (BuildContext context) { + // return CustomForgetPasswordDialog( + // dialogButtonCloseText: "Cancel", + // dialogButtonAcceptText: "YES", + // dialogMessageText: "", + // dialogMessageTextBold: "Are you Sure You want to logout?", + // headingText: "Confirmation", + // showTextField: false, + // acceptFunction: () async{ + // FrequentFunctions().logoutButtonPressed(screenKey.currentState!.context); + // }, + // ); + // }, + // ); + } + + @override + void dispose() { + nameTEC.dispose(); + emailTEC.dispose(); + phoneTEC.dispose(); + addressTEC.dispose(); + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/rota/book_holiday_screen_controller.dart b/lib/controllers/rota/book_holiday_screen_controller.dart new file mode 100644 index 0000000..21239ca --- /dev/null +++ b/lib/controllers/rota/book_holiday_screen_controller.dart @@ -0,0 +1,136 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_calendar_carousel/classes/event.dart'; +import 'package:flutter_calendar_carousel/classes/event_list.dart'; +import 'package:ftc_mobile_app/models/requests/HolidayRequestData.dart'; +import 'package:ftc_mobile_app/models/staffWorkload/StaffWorkloadResponse.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import '../../ftc_mobile_app.dart'; + +class BookHolidayScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final targetDateTime = DateTime.now().obs; + + final agreeToRules = false.obs; + + final holidayStartDate = DateTime.now().obs; + final holidayEndDate = Rx(DateTime.now()); + final holidayDays = 0.obs; + final holidayHours = 0.obs; + + final myWorkLoads = Rx(null); + + final _controller = Get.put(RotaDashboardScreenController()); + + Rx> get markedDatesMap => _controller.markedDatesMap; + + @override + void onInit() { + _calculateDaysAndHours(); + super.onInit(); + } + + @override + void onReady() { + fetchRecords(); + super.onReady(); + } + + onRangeSelect( + DateTime rangeStart, DateTime? rangeEnd, List selectedDates) { + print( + "rangeStart: ${rangeStart.toString()}\nrangeEnd: ${rangeEnd.toString()}"); + + holidayStartDate.value = rangeStart; + + holidayEndDate.value = rangeEnd ?? (rangeStart); + _calculateDaysAndHours(); + } + + _calculateDaysAndHours() { + holidayDays.value = holidayEndDate().difference(holidayStartDate()).inDays; + if (DateFormatter.dateFormatter.format(holidayStartDate()) != + DateFormatter.dateFormatter.format(holidayEndDate())) { + holidayDays.value += 1; + } else { + holidayDays.value = 1; + } + + //Filtering out shifts that fall between the holiday ranges + final shifts = _controller.myShiftsList.where((shift) { + final dateNotNull = shift.shiftDateTime != null; + + final isWithinHolidayRange = + shift.shiftDateTime!.isAfter(holidayStartDate()) && + shift.shiftDateTime!.isBefore(holidayEndDate()); + + final onHolidayStartDate = + DateFormatter.dateFormatter.format(shift.shiftDateTime!) == + DateFormatter.dateFormatter.format(holidayStartDate()); + + final onHolidayEndDate = + DateFormatter.dateFormatter.format(shift.shiftDateTime!) == + DateFormatter.dateFormatter.format(holidayEndDate()); + + return (dateNotNull && + (onHolidayStartDate || isWithinHolidayRange || onHolidayEndDate)); + }).toList(); + + holidayHours.value = shifts.fold( + 0, (previousValue, shift) => (shift.workHrs ?? 0) + previousValue); + + // holidayHours.value = + // holidayEndDate().difference(holidayStartDate()).inHours; + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + Future fetchRecords() async { + var response = await RotaService().getStaffWorkload().showLoader(); + if (response is StaffWorkloadResponse) { + final workLoads = response.data?.staffWorkLoads ?? []; + + if (workLoads.isNotEmpty) { + myWorkLoads.value = workLoads.first; + } + } else if (response is String && response.isNotEmpty) { + FrequentFunctions.showToast(message: response); + } + } + + Future requestHoliday() async { + if (agreeToRules.isFalse) { + FrequentFunctions.showToast( + message: "Please select agree to booking rules first"); + return false; + } + + var response = await RotaService() + .requestHoliday( + request: HolidayRequestData( + hldRqStartDate: holidayStartDate().toUtc().millisecondsSinceEpoch, + hldRqEndDate: holidayEndDate().toUtc().millisecondsSinceEpoch, + hldRqTotalDays: holidayDays(), + hldRqTotalHours: holidayHours(), + hldRqStatus: "pending", + hldRequestType: "holiday", + ), + ) + .showLoader(); + if (response is ResponseModel) { + return true; + } else if (response is String && response.isNotEmpty) { + FrequentFunctions.showToast(message: response); + } + return false; + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/rota/calendar_view_screen_controller.dart b/lib/controllers/rota/calendar_view_screen_controller.dart new file mode 100644 index 0000000..6093944 --- /dev/null +++ b/lib/controllers/rota/calendar_view_screen_controller.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_calendar_carousel/classes/event.dart'; +import 'package:flutter_calendar_carousel/classes/event_list.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import '../../ftc_mobile_app.dart'; + +class CalendarViewScreenController extends GetxController { + GlobalKey scaffoldKey = GlobalKey(); + + RxBool isLoading = false.obs; + + // Rx> markedDatesMap = EventList(events: {},).obs; + Rx targetDateTime = DateTime(2024, 1, 1).obs; + RxString currentMonthName = + DateFormat.yMMM().format(DateTime(2024, 1, 1)).obs; + Rx> events = EventList( + events: {}, + ).obs; + // RxList markedDatesList = RxList(); + RxList datesToShowList = RxList(); + Rx selectedDate = MarkDatesModel.empty().obs; + + // RxList monthWiseRecord = RxList(); + RxList rotaShiftList = RxList(); + + @override + void onInit() { + fetchRecords(); + super.onInit(); + } + + void markDatesOnCalendar() { + // markedDatesList.removeAt(0); //for removing late initialization error + // for (var rotaShift in rotaShiftList) { + // markedDatesList.add( + // MarkDatesModel.addDate(date: rotaShift.shiftTime, title: "Possible")); + // datesToShowList.add(rotaShift); + // } + + // for (var markedDate in markedDatesList) { + // markedDatesMap.value.add( + // DateTime( + // markedDate.date.year, markedDate.date.month, markedDate.date.day), + // Event( + // date: DateTime( + // markedDate.date.year, markedDate.date.month, markedDate.date.day), + // // date: markedDate.date, + // title: markedDate.title, + // icon: markedDate.title == "Possible" + // ? _underlineIcon( + // markedDate.date.day.toString(), + // ) + // : _shiftIcon( + // markedDate.date.day.toString(), + // ), + // ), + // ); + // } + // datesToShowList = datesToShowList; // to ask from panday G + } + + Widget _shiftIcon(String day) => CircleAvatar( + backgroundColor: CustomAppColors.kSecondaryColor, + child: Text( + day, + style: + const TextStyle(color: CustomAppColors.kWhiteColor, fontSize: 13), + ), + ); + + Widget _underlineIcon(String day) => Container( + alignment: Alignment.center, + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide( + color: CustomAppColors.kSecondaryColor, + width: 3.0, + ), + ), + ), + ); + + void onNextMonthTap() { + targetDateTime.value = DateTime(targetDateTime.value.year, + targetDateTime.value.month + 1, targetDateTime.value.day); + currentMonthName.value = DateFormat.yMMM().format(targetDateTime.value); + } + + void onPreviousMonthTap() { + targetDateTime.value = DateTime(targetDateTime.value.year, + targetDateTime.value.month - 1, targetDateTime.value.day); + currentMonthName.value = DateFormat.yMMM().format(targetDateTime.value); + } + + void removeFocus() { + FocusScope.of(scaffoldKey.currentContext!).unfocus(); + } + + Future fetchRecords() async { + isLoading.value = true; + // markedDatesList.add(MarkDatesModel.addDate( + // date: DateTime(2022, 6, 1), + // title: "Possible")); //for removing late initialization error + for (var index = 1; index <= 12; index++) { + var result = await RotaService().serviceUserShifts( + serviceUserId: "65682ad0a01b6c9e6dcde088", month: index, year: 2024); + if (result is MonthWiseRecord) { + // monthWiseRecord.add(result); + rotaShiftList.addAll(FrequentFunctions().findDaysWithData(result)); + } else { + + } + } + + targetDateTime = rotaShiftList.first.shiftTime.obs; + currentMonthName = + DateFormat.yMMM().format(rotaShiftList.first.shiftTime).obs; + isLoading.value = false; + markDatesOnCalendar(); + } +} diff --git a/lib/controllers/rota/export_rota_controller.dart b/lib/controllers/rota/export_rota_controller.dart new file mode 100644 index 0000000..00de2d3 --- /dev/null +++ b/lib/controllers/rota/export_rota_controller.dart @@ -0,0 +1,5 @@ +export 'calendar_view_screen_controller.dart'; +export 'rota_dashboard_screen_controller.dart'; +export 'book_holiday_screen_controller.dart'; +export 'pick_up_shifts_screen_controller.dart'; +export 'your_rota_screen_controller.dart'; \ No newline at end of file diff --git a/lib/controllers/rota/pick_up_shifts_screen_controller.dart b/lib/controllers/rota/pick_up_shifts_screen_controller.dart new file mode 100644 index 0000000..a61ff1f --- /dev/null +++ b/lib/controllers/rota/pick_up_shifts_screen_controller.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/rota/LiveRoasterResponseData.dart'; +import 'package:ftc_mobile_app/models/rota/WeekArrayData.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import '../../ftc_mobile_app.dart'; + +class PickUpShiftsScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final loadingShifts = false.obs; + final myShiftsList = RxList(); + + @override + void onReady() { + getAvailableShifts(); + super.onReady(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + Future getAvailableShifts() async { + loadingShifts.value = true; + + var response = await RotaService().getAvailableShifts(); + if (response is LiveRoasterResponseData) { + myShiftsList.value = response.daysArray ?? []; + } else if (response is String && response.isNotEmpty) { + FrequentFunctions.showToast(message: response); + } + + loadingShifts.value = false; + } + + Future claimShifts(int index, DaysArrayData data) async { + if (data.rosterId!.isNullOrEmpty() || + data.id.isNullOrEmpty() || + (data.locationId?.id).isNullOrEmpty()) return; + + var response = await RotaService() + .claimShift( + rosterId: data.rosterId!, + locationId: data.locationId!.id!, + dayId: data.id!) + .showLoader(); + if (response is LiveRoasterResponseData) { + myShiftsList[index].isRequested = true; + myShiftsList.refresh(); + } else if (response is String && response.isNotEmpty) { + FrequentFunctions.showToast(message: response); + } + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/rota/rota_dashboard_screen_controller.dart b/lib/controllers/rota/rota_dashboard_screen_controller.dart new file mode 100644 index 0000000..83511b0 --- /dev/null +++ b/lib/controllers/rota/rota_dashboard_screen_controller.dart @@ -0,0 +1,102 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_calendar_carousel/classes/event.dart'; +import 'package:flutter_calendar_carousel/classes/event_list.dart'; +import 'package:ftc_mobile_app/models/rota/WeekArrayData.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import '../../ftc_mobile_app.dart'; +import '../../models/rota/LiveRoasterResponseData.dart'; + +class RotaDashboardScreenController extends GetxController + with GetSingleTickerProviderStateMixin { + late TabController tabController = TabController(length: 2, vsync: this); + final GlobalKey screenKey = GlobalKey(); + + final loadingShifts = false.obs; + final myShiftsList = RxList(); + + //it holds shifts list for the selected date + final dateFilteredShiftsList = RxList(); + + final targetDateTime = DateTime.now().obs; + final markedDatesMap = EventList(events: {}).obs; + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void onInit() { + getMyShifts(); + super.onInit(); + } + + Future getMyShifts() async { + loadingShifts.value = true; + + var response = await RotaService().getMyShifts( + startDateMills: DateTime.now().subtract(30.days).millisecondsSinceEpoch, + endDateMills: DateTime.now().add(30.days).millisecondsSinceEpoch); + if (response is LiveRoasterResponseData) { + myShiftsList.value = response.daysArray ?? []; + + createCalendarEvents(myShiftsList()); + + _filterShifts(targetDateTime()); + } else if (response is String && response.isNotEmpty) { + FrequentFunctions.showToast(message: response); + } + + loadingShifts.value = false; + } + + createCalendarEvents(List list) { + final Map> map = {}; + for (final data in list) { + // final date = DateTime.fromMillisecondsSinceEpoch(data.shiftDate!); + + if (data.shiftDateTime == null) return; + + map.addAll({ + DateTime(data.shiftDateTime!.year, data.shiftDateTime!.month, + data.shiftDateTime!.day): [ + Event( + date: DateTime(data.shiftDateTime!.year, data.shiftDateTime!.month, + data.shiftDateTime!.day), + title: data.serviceUserId?.displayName ?? "", + icon: CalendarWidget.underlineIcon(), + ) + ], + }); + } + + markedDatesMap.value.events + ..clear() + ..addAll(map); + + markedDatesMap.refresh(); + } + + onDateSelect(DateTime date, List events) { + targetDateTime.value = date; + + if (events.isNotEmpty) { + _filterShifts(date); + } + } + + _filterShifts(DateTime date) { + dateFilteredShiftsList.value = myShiftsList.where((e) { + return DateFormatter.dateFormatter + .format(DateTime.fromMillisecondsSinceEpoch(e.shiftDate!)) == + DateFormatter.dateFormatter.format(date); + }).toList(); + } + + @override + void dispose() { + tabController.dispose(); + Get.delete(); + super.dispose(); + } +} diff --git a/lib/controllers/rota/your_rota_screen_controller.dart b/lib/controllers/rota/your_rota_screen_controller.dart new file mode 100644 index 0000000..a5609f8 --- /dev/null +++ b/lib/controllers/rota/your_rota_screen_controller.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/rota/LiveRoasterResponseData.dart'; +import 'package:ftc_mobile_app/models/rota/WeekArrayData.dart'; +import 'package:get/get.dart'; + +import '../../ftc_mobile_app.dart'; + +class YourRotaScreenController extends GetxController{ + final GlobalKey screenKey = GlobalKey(); + + final loadingShifts = false.obs; + final myShiftsList = RxList(); + + @override + void onInit() { + getAvailableShifts(); + super.onInit(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + Future getAvailableShifts() async { + loadingShifts.value = true; + + var response = await RotaService().getMyShifts( + startDateMills: + DateTime.now().toUtc().subtract(30.days).millisecondsSinceEpoch, + endDateMills: + DateTime.now().toUtc().add(30.days).millisecondsSinceEpoch); + if (response is LiveRoasterResponseData) { + myShiftsList.value = response.daysArray ?? []; + } else if (response is String && response.isNotEmpty) { + FrequentFunctions.showToast(message: response); + } + + loadingShifts.value = false; + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/dialogs/app_dialogs.dart b/lib/dialogs/app_dialogs.dart new file mode 100644 index 0000000..e956d64 --- /dev/null +++ b/lib/dialogs/app_dialogs.dart @@ -0,0 +1,326 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/appointmentsListResponse/AppointmentsListResponse.dart'; +import 'package:ftc_mobile_app/models/clients/recent_incidents_model.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; + +import 'widgets/appointment_details.dart'; +import 'widgets/recent_incident_detail_dialog.dart'; + +class AppDialog { + static String areYouSureText = "Are you sure?"; + static bool alreadyShownUnauthorizedAlert = false; + + static Widget titleText(String title) { + return CustomTextWidget( + text: title, + textAlign: TextAlign.center, + fontSize: 20, + fontWeight: FontWeight.w700, + ); + } + + static Widget messageText(String message) { + return CustomTextWidget( + text: message, + textAlign: TextAlign.center, + fontSize: 12, + fontWeight: FontWeight.w400, + ); + } + + static Widget buttonsBar({ + String button1Text = "No", + String button2Text = "Yes", + VoidCallback? onButton1Tap, + required VoidCallback onButton2Tap, + }) { + return SizedBox( + width: double.maxFinite, + height: 40.h, + child: Row( + children: [ + Expanded( + child: CustomAppButton( + onTap: () { + if (onButton1Tap == null) { + Get.back(); + } else { + onButton1Tap(); + } + }, + buttonText: button1Text, + textColor: Colors.black, + borderColor: Colors.grey, + buttonColor: Colors.white, + ), + ), + 16.horizontalSpace, + Expanded( + child: CustomAppButton( + onTap: onButton2Tap, + buttonText: button2Text, + buttonColor: Get.theme.primaryColor, + ), + ), + ], + ), + ); + } + + static Future successDialog({ + required String title, + required String message, + required String buttonText, + required VoidCallback onDoneButtonClick, + bool canDismiss = true, + }) { + return Get.generalDialog( + barrierLabel: "", + barrierDismissible: canDismiss, + pageBuilder: (_, a1, a2) { + return AlertDialog( + shape: 15.toRoundedRectRadius(), + backgroundColor: Colors.white, + elevation: 0, + contentPadding: REdgeInsets.symmetric(horizontal: 24, vertical: 32), + content: WillPopScope( + onWillPop: () async => canDismiss, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // SvgPicture.asset( + // Assets.svgIcCheck, + // width: 50.r, + // height: 50.r, + // ), + // 16.verticalSpace, + titleText(title), + 12.verticalSpace, + messageText(message), + 24.verticalSpace, + CustomAppButton( + onTap: onDoneButtonClick, + buttonText: buttonText, + buttonColor: Get.theme.primaryColor, + ).addPaddingHorizontal(24), + ], + ), + ), + ); + }, + ); + } + + static Future showDialogWithSingleActionButton( + {required String title, + required String message, + required VoidCallback onDoneButtonClick}) { + return Get.generalDialog( + barrierLabel: "", + barrierDismissible: true, + pageBuilder: (_, a1, a2) { + return AlertDialog( + shape: 15.toRoundedRectRadius(), + contentPadding: REdgeInsets.symmetric(horizontal: 32, vertical: 32), + backgroundColor: Colors.white, + elevation: 0, + insetPadding: REdgeInsets.all(24), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + 12.verticalSpace, + titleText(title), + 16.verticalSpace, + messageText(message), + 32.verticalSpace, + CustomAppButton( + onTap: onDoneButtonClick, + buttonText: "Done", + buttonColor: Get.theme.primaryColor, + ).addPaddingHorizontal(24), + ], + ), + ); + }, + ); + } + + static Future showDialogDeleteAccount( + {required VoidCallback onDeleteButtonClick}) { + return Get.generalDialog( + barrierLabel: "", + barrierDismissible: true, + pageBuilder: (_, a1, a2) { + return Center( + child: Card( + shape: 15.toRoundedRectRadius(), + color: Colors.white, + margin: REdgeInsets.symmetric(horizontal: 24), + child: Padding( + padding: REdgeInsets.symmetric(horizontal: 24.0, vertical: 32), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(width: double.maxFinite, height: 12.h), + titleText("Delete Account?"), + SizedBox(width: double.maxFinite, height: 16.h), + messageText("Are you sure you want to delete your account?"), + SizedBox(width: double.maxFinite, height: 32.h), + buttonsBar(onButton2Tap: onDeleteButtonClick), + ], + ), + ), + ), + ); + }, + ); + } + + static Future showUnauthorizedAlert() async { + if (alreadyShownUnauthorizedAlert) { + return; + } + + alreadyShownUnauthorizedAlert = true; + await Get.dialog( + WillPopScope( + onWillPop: () async => false, + child: UnconstrainedBox( + child: SizedBox( + width: 1.0.sw, + child: AlertDialog( + shape: 15.toRoundedRectRadius(), + contentPadding: + REdgeInsets.symmetric(horizontal: 32, vertical: 32), + insetPadding: + REdgeInsets.symmetric(horizontal: 24, vertical: 24), + backgroundColor: Colors.white, + content: Column( + children: [ + Icon( + Icons.warning_amber_rounded, + color: Colors.red, + size: 65.r, + ), + 24.verticalSpace, + titleText( + "Session Expired", + ), + 12.verticalSpace, + messageText( + "Your session has been expired. Please log-in again to continue using the app.", + ), + 48.verticalSpace, + CustomAppButton( + onTap: () { + Get.back(); + alreadyShownUnauthorizedAlert = false; + // FrequentFunctions.logout(); + }, + buttonText: "Log In", + buttonColor: Get.theme.primaryColor, + ) + ], + ), + ), + ), + ), + ), + barrierDismissible: false) + .then((value) { + alreadyShownUnauthorizedAlert = false; + }); + } + + static Future alertAndLogout(VoidCallback onTapYes) { + return _showLogoutAlert(onTapYes); + } + + static Future _showLogoutAlert(Function() onTapYes) async { + // do not handle dialog dismiss, as it's already done + return openAlertDialog( + title: "Logout!", + message: "$areYouSureText Do you really want to logout from the app?", + onYesTap: onTapYes); + } + + static Future showAlertAppExit() async { + var isOk = false; + + await Get.generalDialog( + barrierLabel: "", + barrierDismissible: true, + pageBuilder: (_, a1, a2) { + return Center( + child: Card( + shape: 15.toRoundedRectRadius(), + color: Colors.white, + margin: REdgeInsets.symmetric(horizontal: 24), + child: Padding( + padding: REdgeInsets.symmetric(horizontal: 24.0, vertical: 32), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(width: double.maxFinite, height: 12.h), + titleText(areYouSureText), + SizedBox(width: double.maxFinite, height: 16.h), + titleText("Do you really want to exit from the app?"), + SizedBox(width: double.maxFinite, height: 32.h), + buttonsBar(onButton2Tap: () async { + isOk = true; + Get.back(); + }), + ], + ), + ), + ), + ); + }, + ); + + return isOk; + } + + static Future openAlertDialog( + {required String title, + required String message, + required VoidCallback onYesTap}) { + return Get.generalDialog( + barrierLabel: "", + barrierDismissible: true, + pageBuilder: (_, a1, a2) { + return Center( + child: Card( + shape: 15.toRoundedRectRadius(), + color: Colors.white, + margin: REdgeInsets.symmetric(horizontal: 24), + child: Padding( + padding: REdgeInsets.symmetric(horizontal: 24.0, vertical: 32), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(width: double.maxFinite, height: 12.h), + titleText(title), + 16.verticalSpace, + messageText(message), + SizedBox(width: double.maxFinite, height: 32.h), + buttonsBar(onButton2Tap: onYesTap), + ], + ), + ), + ), + ); + }); + } + + static showAppointmentDetailDialog( + {required AppointmentsListResponseData data}) { + Get.dialog(AppointmentDetailsDialog(data: data)); + } + + static showRecentIncidentDetailDialog({required RecentIncidentsModel data}) { + Get.dialog(RecentIncidentDetailDialog(data: data)); + } +} diff --git a/lib/dialogs/widgets/appointment_details.dart b/lib/dialogs/widgets/appointment_details.dart new file mode 100644 index 0000000..e315cb6 --- /dev/null +++ b/lib/dialogs/widgets/appointment_details.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/appointmentsListResponse/AppointmentsListResponse.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/common_cancel_button.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/my_circle_image.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; + +class AppointmentDetailsDialog extends StatelessWidget { + final AppointmentsListResponseData data; + + const AppointmentDetailsDialog({super.key, required this.data}); + + @override + Widget build(BuildContext context) { + final appointmentDate = DateFormat("dd/MM/yyyy") + .format(DateTime.fromMillisecondsSinceEpoch(data.appointmentDate!)); + + final appointmentTime = + "${data.appointmentStartTime ?? "NA"} to ${data.appointmentEndTime ?? "NA"}"; + + final d = (data.appointmentMin ?? 0).minutes; + final duration = + "${d.inHours} hours and ${d.inMinutes - (d.inHours.hours.inMinutes)} minutes"; + + return AlertDialog( + insetPadding: REdgeInsets.all(18), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.r)), + backgroundColor: Colors.white, + surfaceTintColor: Colors.white, + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + data.appointmentTitle ?? "", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.sp, + ), + textAlign: TextAlign.left, + ), + CustomTextWidget( + text: "$appointmentDate $appointmentTime", + fontSize: 13.sp, + textAlign: TextAlign.left, + ), + 16.verticalSpace, + CustomTextWidget( + text: "Appointment duration:", + fontSize: 14.sp, + fontColor: Colors.black, + fontWeight: FontWeight.w500, + textAlign: TextAlign.left, + ), + CustomTextWidget( + text: duration, + fontSize: 13.sp, + textAlign: TextAlign.left, + ), + 16.verticalSpace, + Text.rich( + TextSpan( + text: "Detail: ", + style: TextStyle( + fontSize: 14.sp, + color: Colors.black, + fontWeight: FontWeight.w500, + ), + children: [ + TextSpan( + text: data.appointmentDetails ?? "", + style: TextStyle( + fontSize: 13.sp, + fontWeight: FontWeight.w400, + ), + ) + ]), + ), + 16.verticalSpace, + CustomTextWidget( + text: "Staff:", + fontSize: 14.sp, + fontColor: Colors.black, + fontWeight: FontWeight.w500, + textAlign: TextAlign.left, + ), + 8.verticalSpace, + Row( + children: [ + MyCircleImage( + imageSize: 32.r, + url: "${WebUrls.baseUrl}${data.staff?.profilePictureUrl ?? ""}", + errorWidget: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + height: 32.r, + width: 32.r, + ), + ), + 8.horizontalSpace, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + data.staff?.name ?? "", + style: TextStyle( + fontSize: 13.sp, + fontWeight: FontWeight.w500 + ), + ), + Text( + "Contact No. ${data.staff?.phoneNumber ?? ""}", + style: TextStyle( + fontSize: 13.sp, + ), + ), + ], + ), + ], + ), + ], + ), + actions: const [CommonCloseTextButton()], + ); + } +} diff --git a/lib/dialogs/widgets/holiday_request_sent_dialog.dart b/lib/dialogs/widgets/holiday_request_sent_dialog.dart new file mode 100644 index 0000000..11ae3de --- /dev/null +++ b/lib/dialogs/widgets/holiday_request_sent_dialog.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/common_cancel_button.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/label_value_box_widget.dart'; + +class HolidayRequestSentDialog extends StatelessWidget { + const HolidayRequestSentDialog({ + super.key, + required this.holidayStartDate, + required this.holidayEndDate, + required this.holidayTotalTime, + }); + + final String holidayStartDate; + final String holidayEndDate; + final String holidayTotalTime; + + @override + Widget build(BuildContext context) { + return AlertDialog( + insetPadding: REdgeInsets.all(18), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.r)), + backgroundColor: Colors.white, + surfaceTintColor: Colors.white, + title: Center( + child: CustomTextWidget( + text: 'Your Holiday Request has been sent', + fontWeight: FontWeight.bold, + isExpanded: false, + alignment: Alignment.center, + fontSize: 16.sp, + ), + ), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + flex: 1, + child: LabelValueBoxWidget( + label: 'Start Date:', + value: holidayStartDate, + borderColor: + CustomAppColors.kLightGreyColor.withOpacity(0.3), + )), + 5.horizontalSpace, + Expanded( + flex: 1, + child: LabelValueBoxWidget( + label: 'End Date', + value: holidayEndDate, + borderColor: + CustomAppColors.kLightGreyColor.withOpacity(0.3), + )), + ], + ), + 10.verticalSpace, + SizedBox( + width: MediaQuery.of(context).size.width, + child: LabelValueBoxWidget( + label: 'Your remaining Holidays', + value: holidayTotalTime, + borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + )), + 10.verticalSpace, + const CustomTextWidget( + text: "Kindly wait as we review your holiday request.") + ], + ), + actions: const [CommonCloseTextButton()], + ); + } +} diff --git a/lib/dialogs/widgets/holidays_data_dialog.dart b/lib/dialogs/widgets/holidays_data_dialog.dart new file mode 100644 index 0000000..a7e0a8f --- /dev/null +++ b/lib/dialogs/widgets/holidays_data_dialog.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/common_cancel_button.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/label_value_box_widget.dart'; + +class HolidaysDataDialog extends StatelessWidget { + const HolidaysDataDialog({ + super.key, + required this.holidayModel, + }); + + final HolidayModel holidayModel; + + @override + Widget build(BuildContext context) { + return AlertDialog( + insetPadding: REdgeInsets.all(18), + surfaceTintColor: CustomAppColors.kPrimaryColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2)), + backgroundColor: CustomAppColors.kWhiteColor, + title: Center( + child: CustomTextWidget( + text: 'Your Holidays', + fontWeight: FontWeight.w700, + isExpanded: false, + alignment: Alignment.center, + fontSize: 16.sp, + ), + ), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + LabelValueBoxWidget( + label: 'Carried Over:', + value: holidayModel.carriedOver, + borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + ), + 10.verticalSpace, + LabelValueBoxWidget( + label: 'Holiday Entitlement', + value: holidayModel.holidayEntitlement, + borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + ), + 10.verticalSpace, + LabelValueBoxWidget( + label: 'Holiday Allowance', + value: holidayModel.holidayAllowance, + borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + ), + 10.verticalSpace, + LabelValueBoxWidget( + label: 'Your remaining Holidays', + value: holidayModel.remainingHolidays, + borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + ), + 10.verticalSpace, + LabelValueBoxWidget( + label: 'Time left before Year End:', + value: holidayModel.timeLeftBeforeYearEnd, + borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + ), + 10.verticalSpace, + const CommonCloseTextButton() + ], + ), + ); + } +} diff --git a/lib/dialogs/widgets/recent_incident_detail_dialog.dart b/lib/dialogs/widgets/recent_incident_detail_dialog.dart new file mode 100644 index 0000000..a1748b3 --- /dev/null +++ b/lib/dialogs/widgets/recent_incident_detail_dialog.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/recent_incidents_model.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/common_cancel_button.dart'; +import 'package:quill_html_editor/quill_html_editor.dart'; + +class RecentIncidentDetailDialog extends StatelessWidget { + final RecentIncidentsModel data; + + const RecentIncidentDetailDialog({super.key, required this.data}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + insetPadding: REdgeInsets.all(18), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.r)), + backgroundColor: Colors.white, + surfaceTintColor: Colors.white, + contentPadding: EdgeInsets.zero, + content: SizedBox( + width: double.maxFinite, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + 20.verticalSpace, + Text("Incident Date - Time", + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 14.sp, + color: Colors.black, + )).addPaddingHorizontal(16), + Text( + DateFormatter.ddMMyyyyhhmmFormat( + DateTime.parse(data.createdAt).toLocal()), + style: TextStyle( + fontWeight: FontWeight.w400, + fontSize: 12.sp, + color: CustomAppColors.kBlackColor, + )).addPaddingHorizontal(16), + 8.verticalSpace, + Divider( + color: CustomAppColors.kLightGreyColor, + ), + 8.verticalSpace, + CustomTextWidget( + text: data.incidentTitle.isNotEmpty + ? data.incidentTitle + : "Untitled Incident", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + ).addPaddingHorizontal(16), + 4.verticalSpace, + Expanded( + child: QuillHtmlEditor( + text: data.note, + hintText: 'Hint text goes here', + controller: data.quillController, + isEnabled: false, + ensureVisible: false, + minHeight: 50.h, + autoFocus: false, + textStyle: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w400, + ), + // hintTextStyle: _hintTextStyle, + hintTextAlign: TextAlign.start, + loadingBuilder: (context) { + return const Center( + child: CircularProgressIndicator( + strokeWidth: 1, + color: Colors.red, + )); + }, + ).addPaddingHorizontal(16), + ), + const CommonCloseTextButton().addPaddingAll(16), + 8.verticalSpace, + ], + ), + )); + } +} diff --git a/lib/dialogs/widgets/shift_dialog.dart b/lib/dialogs/widgets/shift_dialog.dart new file mode 100644 index 0000000..81fc99a --- /dev/null +++ b/lib/dialogs/widgets/shift_dialog.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/rota/WeekArrayData.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/common_cancel_button.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/label_value_box_widget.dart'; + +class ShowRotaAlertDialog extends StatelessWidget { + final DaysArrayData data; + final Function? onClaimShiftTap; + final Function? onCancelShiftTap; + + const ShowRotaAlertDialog( + {super.key, + required this.data, + this.onClaimShiftTap, + this.onCancelShiftTap}); + + @override + Widget build(BuildContext context) { + final isAvailableShift = (data.staffUserId == null); + return AlertDialog( + insetPadding: REdgeInsets.all(18), + contentPadding: REdgeInsets.all(15), + surfaceTintColor: CustomAppColors.kPrimaryColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.r)), + backgroundColor: CustomAppColors.kPrimaryColor, + title: Center( + child: CustomTextWidget( + text: isAvailableShift + ? 'Available Shift' + : data.staffUserId?.staffMemberName ?? "", + fontWeight: FontWeight.bold, + isExpanded: false, + alignment: Alignment.center, + fontSize: 16.sp, + ), + ), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + LabelValueBoxWidget( + label: 'Service User (Patient):', + value: data.serviceUserId?.displayName ?? 'Unassigned', + borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + ), + 10.verticalSpace, + // LabelValueBoxWidget( + // label: 'Worker Type:', + // value: data.workerType, + // borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + // ), + // 10.verticalSpace, + LabelValueBoxWidget( + label: 'Location:', + value: data.locationId?.shiftLocationName ?? '', + borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + ), + // 10.verticalSpace, + // LabelValueBoxWidget( + // label: 'Staff Required:', + // value: data.staffRequired, + // borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + // ), + 10.verticalSpace, + LabelValueBoxWidget( + label: 'Start Time:', + value: DateFormatter() + .roasterShiftFormattedTime(time: data.shiftStartTime ?? ""), + borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + ), + 10.verticalSpace, + LabelValueBoxWidget( + label: 'End Time:', + value: DateFormatter() + .roasterShiftFormattedTime(time: data.shiftEndTime ?? ""), + borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + ), + 10.verticalSpace, + // LabelValueBoxWidget( + // label: 'Break Time:', + // value: data.breakTime, + // borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + // ), + // 10.verticalSpace, + LabelValueBoxWidget( + label: 'Notes:', + value: data.note ?? "NA", + borderColor: CustomAppColors.kLightGreyColor.withOpacity(0.3), + ), + 10.verticalSpace, + ConstrainedBox( + constraints: BoxConstraints(minHeight: 30.h, maxHeight: 30.h), + child: Row( + children: [ + data.isRequested == true + ? FrequentFunctions.noWidget + : Expanded( + child: CustomAppButton( + onTap: () { + Navigator.of(context).pop(); + + if (isAvailableShift) { + if (onClaimShiftTap != null) { + onClaimShiftTap!.call(); + } + } else { + if (onCancelShiftTap != null) { + onCancelShiftTap!.call(); + } + } + }, + buttonText: + isAvailableShift ? "Claim Shift" : "Cancel Shift", + textColor: Colors.white, + borderColor: Colors.transparent, + ), + ), + 10.horizontalSpace, + const Expanded(child: CommonCloseTextButton()), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart new file mode 100644 index 0000000..fa22502 --- /dev/null +++ b/lib/firebase_options.dart @@ -0,0 +1,68 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for web - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for macos - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.windows: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for windows - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyDF2vZbiWQROGvyvLeeCmAjPLnPLYjE6Os', + appId: '1:583559514958:android:89af243ca4a3888a32ec1f', + messagingSenderId: '583559514958', + projectId: 'ftc-services-ea8d6', + storageBucket: 'ftc-services-ea8d6.appspot.com', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'AIzaSyAkJyvTX8oQSY9Ju3L39oupZbdy4Eo-RbA', + appId: '1:583559514958:ios:5ec9cd88ca24777932ec1f', + messagingSenderId: '583559514958', + projectId: 'ftc-services-ea8d6', + storageBucket: 'ftc-services-ea8d6.appspot.com', + iosBundleId: 'com.ftc.app.ftcMobileApp', + ); +} diff --git a/lib/ftc_mobile_app.dart b/lib/ftc_mobile_app.dart new file mode 100644 index 0000000..f0fd293 --- /dev/null +++ b/lib/ftc_mobile_app.dart @@ -0,0 +1,6 @@ +export 'models/export_models.dart'; +export 'view/export_view.dart'; +export 'controllers/export_controllers.dart'; +export 'utilities/export_utilities.dart'; +export 'web_services/export_web_services.dart'; +export 'package:flutter_screenutil/flutter_screenutil.dart'; \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..1a17414 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; +import 'package:get_time_ago/get_time_ago.dart'; + +import 'utilities/app_session_manager.dart'; +import 'utilities/custom_timeago_messages.dart'; +import 'utilities/fcm_notifications.dart'; +import 'utilities/notification_util.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await CustomUIOverLay.initialize(); + GetTimeAgo.setCustomLocaleMessages('en', CustomTimeAgoMessages()); + + //Notification initialize + NotificationUtils.init(); + try { + await FcmNotification.getInstance().init(); + } catch (e) { + debugPrint("FcmNotification init error: $e"); + } + + await LocalStorageManager.init(); + Get.lazyPut(() => AppSessionManager(), fenix: true); + + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return ScreenUtilInit( + designSize: Size( + MediaQuery.sizeOf(context).width, + MediaQuery.sizeOf(context).height, + ), + builder: (_, child) { + return GetMaterialApp( + title: ConstantText.kAppName, + debugShowCheckedModeBanner: false, + builder: (context, child) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaler: const TextScaler.linear(1.0), + ), + child: child!, + ); + }, + theme: CustomTheme.defaultTheme(), + navigatorKey: CustomRouteGenerator.navigatorKey, + initialRoute: CustomRouteNames.kInitialRoute, + onGenerateRoute: CustomRouteGenerator.generateRoute, + ); + }, + ); + } +} diff --git a/lib/models/appointmentsListResponse/AppointmentsListResponse.dart b/lib/models/appointmentsListResponse/AppointmentsListResponse.dart new file mode 100644 index 0000000..45d523b --- /dev/null +++ b/lib/models/appointmentsListResponse/AppointmentsListResponse.dart @@ -0,0 +1,103 @@ +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; + +class AppointmentsListResponse { + AppointmentsListResponse({ + this.success, + this.status, + this.message, + this.data,}); + + AppointmentsListResponse.fromJson(dynamic json) { + status = json['status']; + message = json['message']; + if (json['data'] != null) { + data = []; + json['data'].forEach((v) { + data?.add(AppointmentsListResponseData.fromJson(v)); + }); + } + } + + bool? success; + String? status; + String? message; + List? data; + + Map toJson() { + final map = {}; + map['status'] = status; + map['message'] = message; + if (data != null) { + map['data'] = data?.map((v) => v.toJson()).toList(); + } + return map; + } + +} + +class AppointmentsListResponseData { + AppointmentsListResponseData({ + this.id, + this.user, + this.appointmentDate, + this.appointmentStartTime, + this.appointmentEndTime, + this.appointmentMin, + this.staff, + this.addedby, + this.appointmentDetails, + this.status, + this.createdAt, + this.updatedAt, + this.appointmentTitle,}); + + AppointmentsListResponseData.fromJson(dynamic json) { + id = json['_id']; + user = json['user']; + appointmentDate = json['appointmentDate']; + appointmentStartTime = json['appointmentStartTime']; + appointmentEndTime = json['appointmentEndTime']; + appointmentMin = json['appointmentMin']; + staff = json['staff'] != null ? UserData.fromJson(json['staff']) : null; + addedby = json['addedby']; + appointmentDetails = json['appointmentDetails']; + status = json['status']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + appointmentTitle = json['appointmentTitle']; + } + String? id; + String? user; + int? appointmentDate; + String? appointmentStartTime; + String? appointmentEndTime; + int? appointmentMin; + UserData? staff; + String? addedby; + String? appointmentDetails; + String? status; + String? createdAt; + String? updatedAt; + String? appointmentTitle; + + Map toJson() { + final map = {}; + map['_id'] = id; + map['user'] = user; + map['appointmentDate'] = appointmentDate; + map['appointmentStartTime'] = appointmentStartTime; + map['appointmentEndTime'] = appointmentEndTime; + map['appointmentMin'] = appointmentMin; + if (staff != null) { + map['staff'] = staff?.toJson(); + } + map['addedby'] = addedby; + map['appointmentDetails'] = appointmentDetails; + map['status'] = status; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + map['appointmentTitle'] = appointmentTitle; + return map; + } + +} \ No newline at end of file diff --git a/lib/models/chat/ChatModel.dart b/lib/models/chat/ChatModel.dart new file mode 100644 index 0000000..dff501a --- /dev/null +++ b/lib/models/chat/ChatModel.dart @@ -0,0 +1,80 @@ +import '../profileData/user_data.dart'; + +class ChatModel { + static const stateNone = 0; + static const stateError = -1; + static const stateLoading = 1; + static const stateSuccess = 2; + + static const String fileTypeLocalPath = "localPath"; + + ChatModel({ + this.id, + this.from, + this.to, + this.message, + this.messageType, + this.filePath, + this.date, + this.archived, + this.createdAt, + this.updatedAt, + this.localId, + this.fileType, + this.state = stateNone, + }); + + ChatModel.fromJson(dynamic json) { + from = json['from'] != null ? UserData.fromJson(json['from']) : null; + to = json['to'] != null ? UserData.fromJson(json['to']) : null; + id = json['_id']; + message = json['message']; + messageType = json['messageType']; + filePath = json['filePath']; + date = json['date']; + localId = json['localId']; + archived = json['archived']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + + date = DateTime.tryParse(createdAt ?? "")?.millisecondsSinceEpoch ?? 0; + } + + String? id; + UserData? from; + UserData? to; + String? message; + String? messageType; + String? filePath; + int? date; + bool? archived; + String? createdAt; + String? updatedAt; + + //Local usage variables + int state = stateNone; + String? fileType; + String? localId; + + Map toJson() { + final map = {}; + if (from != null) { + map['from'] = from?.toJson(); + } + if (to != null) { + map['to'] = to?.toJson(); + } + map['_id'] = id; + map['message'] = message; + map['messageType'] = messageType; + map['filePath'] = filePath; + map['date'] = date; + map['archived'] = archived; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + map['localId'] = localId; + map['state'] = state; + map['isSent'] = state; + return map; + } +} diff --git a/lib/models/chat/add_group_message_model.dart b/lib/models/chat/add_group_message_model.dart new file mode 100644 index 0000000..1aa339a --- /dev/null +++ b/lib/models/chat/add_group_message_model.dart @@ -0,0 +1,61 @@ +class AddDeleteUpdateGroupMessageModel { + AddDeleteUpdateGroupMessageModel({ + required this.groupId, + required this.userId, + required this.message, + required this.isDeleted, + required this.isHide, + required this.isPin, + required this.id, + required this.seenBy, + required this.createdAt, + required this.updatedAt, + required this.v, + }); + String groupId = ""; + String userId = ""; + String message = ""; + bool isDeleted = false; + bool isHide = false; + bool isPin = false; + String id = ""; + List seenBy = []; + String createdAt = ""; + String updatedAt = ""; + int v = -1; + + AddDeleteUpdateGroupMessageModel.fromJson(Map json){ + groupId = json['groupId']??""; + userId = json['userId']??""; + message = json['message']??""; + isDeleted = json['isDeleted']?? false; + isHide = json['isHide']?? false; + isPin = json['isPin']?? false; + id = json['_id']??""; + seenBy = List.castFrom(json['seenBy']??[]); + createdAt = json['createdAt']??""; + updatedAt = json['updatedAt']??""; + v = json['__v']??-1; + } + + Map toJson() { + final data = {}; + data['groupId'] = groupId; + data['userId'] = userId; + data['message'] = message; + data['isDeleted'] = isDeleted; + data['isHide'] = isHide; + data['isPin'] = isPin; + data['_id'] = id; + data['seenBy'] = seenBy; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + data['__v'] = v; + return data; + } + + @override + String toString() { + return 'AddDeleteUpdateGroupMessageModel{groupId: $groupId, userId: $userId, message: $message, isDeleted: $isDeleted, isHide: $isHide, isPin: $isPin, id: $id, seenBy: $seenBy, createdAt: $createdAt, updatedAt: $updatedAt, v: $v}'; + } +} \ No newline at end of file diff --git a/lib/models/chat/all_group_messages_model.dart b/lib/models/chat/all_group_messages_model.dart new file mode 100644 index 0000000..3d7fcf8 --- /dev/null +++ b/lib/models/chat/all_group_messages_model.dart @@ -0,0 +1,353 @@ +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; + +import '../profileData/FcmTokens.dart'; +import '../profileData/LocationData.dart'; + +class AllGroupMessages { + AllGroupMessages({ + required this.id, + required this.groupId, + required this.userId, + required this.message, + required this.messageType, + required this.filePath, + required this.isDeleted, + required this.isHide, + required this.isPin, + required this.seenBy, + required this.createdAt, + required this.updatedAt, + required this.v, + }); + + String id = ""; + GroupId groupId = GroupId.empty(); + UserData? userId; + String message = ""; + String messageType = ""; + String filePath = ""; + bool isDeleted = false; + bool isHide = false; + bool isPin = false; + List seenBy = []; + String createdAt = ""; + String updatedAt = ""; + int v = -1; + + AllGroupMessages.empty(); + + AllGroupMessages.fromJson(Map json) { + id = json['_id'] ?? ""; + groupId = GroupId.fromJson(json['groupId'] ?? GroupId.empty()); + userId = UserData.fromJson(json['userId'] ?? {}); + message = json['message'] ?? ""; + messageType = json['messageType'] ?? ""; + filePath = json['filePath'] ?? ""; + isDeleted = json['isDeleted'] ?? false; + isHide = json['isHide'] ?? false; + isPin = json['isPin'] ?? false; + seenBy = List.castFrom(json['seenBy'] ?? []); + createdAt = json['createdAt'] ?? ""; + updatedAt = json['updatedAt'] ?? ""; + } + + Map toJson() { + final data = {}; + data['_id'] = id; + data['groupId'] = groupId.toJson(); + data['userId'] = userId?.toJson(); + data['message'] = message; + data['messageType'] = messageType; + data['filePath'] = filePath; + data['isDeleted'] = isDeleted; + data['isHide'] = isHide; + data['isPin'] = isPin; + data['seenBy'] = seenBy; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + return data; + } + + @override + String toString() { + return 'AllGroupMessages{id: $id, groupId: $groupId, userId: $userId, message: $message, isDeleted: $isDeleted, isHide: $isHide, isPin: $isPin, seenBy: $seenBy, createdAt: $createdAt, updatedAt: $updatedAt, v: $v}'; + } +} + +class GroupId { + GroupId({ + required this.lastMessages, + required this.groupWorkingScheduleTime, + required this.id, + required this.groupName, + required this.groupImage, + required this.groupMembers, + required this.isGroup, + required this.date, + required this.isActive, + required this.createdAt, + required this.updatedAt, + required this.v, + }); + + LastMessages lastMessages = LastMessages.empty(); + GroupWorkingScheduleTime groupWorkingScheduleTime = + GroupWorkingScheduleTime.empty(); + String id = ""; + String groupName = ""; + String groupImage = ""; + List groupMembers = []; + bool isGroup = false; + String date = ""; + bool isActive = false; + String createdAt = ""; + String updatedAt = ""; + int v = -1; + + GroupId.empty(); + + GroupId.id({required this.id}); + + GroupId.fromJson(Map json) { + lastMessages = + LastMessages.fromJson(json['lastMessages'] ?? LastMessages.empty()); + groupWorkingScheduleTime = GroupWorkingScheduleTime.fromJson( + json['groupWorkingScheduleTime'] ?? GroupWorkingScheduleTime.empty()); + id = json['_id'] ?? ""; + groupName = json['groupName'] ?? ""; + groupImage = json['groupImage'] ?? ""; + groupMembers = List.castFrom(json['groupMembers'] ?? []); + isGroup = json['isGroup'] ?? false; + date = json['date'] ?? ""; + isActive = json['isActive'] ?? false; + createdAt = json['createdAt'] ?? ""; + updatedAt = json['updatedAt'] ?? ""; + v = json['__v'] ?? -1; + } + + Map toJson() { + final data = {}; + data['lastMessages'] = lastMessages.toJson(); + data['groupWorkingScheduleTime'] = groupWorkingScheduleTime.toJson(); + data['_id'] = id; + data['groupName'] = groupName; + data['groupImage'] = groupImage; + data['groupMembers'] = groupMembers; + data['isGroup'] = isGroup; + data['date'] = date; + data['isActive'] = isActive; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + data['__v'] = v; + return data; + } + + @override + String toString() { + return 'GroupId{lastMessages: $lastMessages, groupWorkingScheduleTime: $groupWorkingScheduleTime, id: $id, groupName: $groupName, groupImage: $groupImage, groupMembers: $groupMembers, isGroup: $isGroup, date: $date, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt, v: $v}'; + } +} + +class LastMessages { + LastMessages({ + required this.message, + required this.messageSentBy, + required this.messageTime, + }); + + String message = ""; + String messageSentBy = ""; + int messageTime = -1; + + LastMessages.empty(); + + LastMessages.fromJson(Map json) { + message = json['message'] ?? ""; + messageSentBy = json['messageSentBy'] ?? ""; + messageTime = json['messageTime'] ?? -1; + } + + Map toJson() { + final data = {}; + data['message'] = message; + data['messageSentBy'] = messageSentBy; + data['messageTime'] = messageTime; + return data; + } + + @override + String toString() { + return 'LastMessages{message: $message, messageSentBy: $messageSentBy, messageTime: $messageTime}'; + } +} + +class GroupWorkingScheduleTime { + GroupWorkingScheduleTime({ + required this.startTime, + required this.endTime, + required this.totalWorkHours, + }); + + int startTime = -1; + int endTime = -1; + String totalWorkHours = ""; + + GroupWorkingScheduleTime.empty(); + + GroupWorkingScheduleTime.fromJson(Map json) { + startTime = json['startTime'] ?? -1; + endTime = json['endTime'] ?? -1; + totalWorkHours = json['totalWorkHours'] ?? ""; + } + + Map toJson() { + final data = {}; + data['startTime'] = startTime; + data['endTime'] = endTime; + data['totalWorkHours'] = totalWorkHours; + return data; + } + + @override + String toString() { + return 'GroupWorkingScheduleTime{startTime: $startTime, endTime: $endTime, totalWorkHours: $totalWorkHours}'; + } +} + +class UserId { + UserId({ + required this.fcmTokens, + required this.location, + required this.id, + required this.userModelName, + required this.name, + required this.version, + required this.email, + required this.phoneNumber, + required this.active, + required this.role, + required this.profilePictureUrl, + required this.deviceId, + required this.verificationCode, + required this.isVerified, + required this.approved, + required this.blocked, + required this.createdAt, + required this.updatedAt, + required this.v, + required this.password, + required this.userSettings, + required this.modelId, + }); + + FcmTokens fcmTokens = FcmTokens.empty(); + LocationData location = LocationData.empty(); + String id = ""; + String userModelName = ""; + String name = ""; + String version = ""; + String email = ""; + String phoneNumber = ""; + bool active = false; + String role = ""; + String profilePictureUrl = ""; + String deviceId = ""; + String verificationCode = ""; + bool isVerified = false; + bool approved = false; + bool blocked = false; + String createdAt = ""; + String updatedAt = ""; + int v = -1; + String password = ""; + String userSettings = ""; + String modelId = ""; + + UserId.empty(); + + UserId.id({required this.id}); + + UserId.fromJson(Map json) { + fcmTokens = FcmTokens.fromJson(json['fcm_tokens'] ?? FcmTokens.empty()); + location = LocationData.fromJson(json['location'] ?? LocationData.empty()); + id = json['_id'] ?? ""; + userModelName = json['userModelName'] ?? ""; + name = json['name'] ?? ""; + version = json['version'] ?? ""; + email = json['email'] ?? ""; + phoneNumber = json['phoneNumber'] ?? ""; + active = json['active'] ?? false; + role = json['role'] ?? ""; + profilePictureUrl = json['profile_picture_url'] ?? ""; + deviceId = json['deviceId'] ?? ""; + verificationCode = json['verification_code'] ?? ""; + isVerified = json['is_verified'] ?? false; + approved = json['approved'] ?? false; + blocked = json['blocked'] ?? false; + createdAt = json['createdAt'] ?? ""; + updatedAt = json['updatedAt'] ?? ""; + v = json['__v'] ?? -1; + password = json['password'] ?? ""; + userSettings = json['userSettings'] ?? ""; + modelId = json['modelId'] ?? ""; + } + + Map toJson() { + final data = {}; + data['fcm_tokens'] = fcmTokens.toJson(); + data['location'] = location.toJson(); + data['_id'] = id; + data['userModelName'] = userModelName; + data['name'] = name; + data['version'] = version; + data['email'] = email; + data['phoneNumber'] = phoneNumber; + data['active'] = active; + data['role'] = role; + data['profile_picture_url'] = profilePictureUrl; + data['deviceId'] = deviceId; + data['verification_code'] = verificationCode; + data['is_verified'] = isVerified; + data['approved'] = approved; + data['blocked'] = blocked; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + data['__v'] = v; + data['password'] = password; + data['userSettings'] = userSettings; + data['modelId'] = modelId; + return data; + } + + @override + String toString() { + return 'UserId{fcmTokens: $fcmTokens, location: $location, id: $id, userModelName: $userModelName, name: $name, version: $version, email: $email, phoneNumber: $phoneNumber, active: $active, role: $role, profilePictureUrl: $profilePictureUrl, deviceId: $deviceId, verificationCode: $verificationCode, isVerified: $isVerified, approved: $approved, blocked: $blocked, createdAt: $createdAt, updatedAt: $updatedAt, v: $v, password: $password, userSettings: $userSettings, modelId: $modelId}'; + } +} + +// class Location { +// Location({ +// required this.type, +// required this.coordinates, +// }); +// String type = ""; +// List coordinates = []; +// +// Location.empty(); +// Location.fromJson(Map json){ +// type = json['type']??""; +// coordinates = List.castFrom(json['coordinates']??[]); +// } +// +// Map toJson() { +// final data = {}; +// data['type'] = type; +// data['coordinates'] = coordinates; +// return data; +// } +// +// @override +// String toString() { +// return 'Location{type: $type, coordinates: $coordinates}'; +// } +// } diff --git a/lib/models/chat/all_single_chat_message_model.dart b/lib/models/chat/all_single_chat_message_model.dart new file mode 100644 index 0000000..f3205a4 --- /dev/null +++ b/lib/models/chat/all_single_chat_message_model.dart @@ -0,0 +1,45 @@ +class AllSingleChatMessages { + AllSingleChatMessages({ + required this.seen, + required this.recieverId, + required this.name, + required this.message, + required this.date, + required this.image, + required this.senderId, + }); + bool seen = false; + String recieverId =""; + String name =""; + String message =""; + String date =""; + String image =""; + String senderId =""; + + AllSingleChatMessages.fromJson(Map json){ + seen = json['seen'] ?? false; + recieverId = json['recieverId'] ?? ""; + name = json['name'] ?? ""; + message = json['message'] ?? ""; + date = json['date'] ?? ""; + image = json['image'] ?? ""; + senderId = json['senderId'] ?? ""; + } + + Map toJson() { + final data = {}; + data['seen'] = seen; + data['recieverId'] = recieverId; + data['name'] = name; + data['message'] = message; + data['date'] = date; + data['image'] = image; + data['senderId'] = senderId; + return data; + } + + @override + String toString() { + return 'AllSingleChatMessages{seen: $seen, recieverId: $recieverId, name: $name, message: $message, date: $date, image: $image, senderId: $senderId}'; + } +} \ No newline at end of file diff --git a/lib/models/chat/all_single_user_chat_server_side.dart b/lib/models/chat/all_single_user_chat_server_side.dart new file mode 100644 index 0000000..1cf3d6b --- /dev/null +++ b/lib/models/chat/all_single_user_chat_server_side.dart @@ -0,0 +1,57 @@ +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; + +class AllSingleUsersChats { + AllSingleUsersChats({ + required this.from, + required this.to, + required this.message, + required this.seen, + required this.isDeleted, + required this.isHide, + required this.createdAt, + required this.updatedAt, + required this.seenAt, + required this.id, + }); + + UserData? from; + UserData? to; + String message = ""; + bool seen = false; + bool isDeleted = false; + bool isHide = false; + String createdAt = ""; + String updatedAt = ""; + String seenAt = ""; + String id = ""; + + AllSingleUsersChats.empty(); + + AllSingleUsersChats.fromJson(Map json) { + from = UserData.fromJson(json['from'] ?? {}); + to = UserData.fromJson(json['to'] ?? {}); + message = json['message'] ?? ""; + seen = json['seen'] ?? false; + isDeleted = json['isDeleted'] ?? false; + isHide = json['isHide'] ?? false; + createdAt = json['createdAt'] ?? ""; + updatedAt = json['updatedAt'] ?? ""; + seenAt = json['seenAt'] ?? ""; + id = json['_id'] ?? ""; + } + + Map toJson() { + final data = {}; + data['from'] = from?.toJson(); + data['to'] = to?.toJson(); + data['message'] = message; + data['seen'] = seen; + data['isDeleted'] = isDeleted; + data['isHide'] = isHide; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + data['seenAt'] = seenAt; + data['_id'] = id; + return data; + } +} diff --git a/lib/models/chat/combined_last_messages_model_class.dart b/lib/models/chat/combined_last_messages_model_class.dart new file mode 100644 index 0000000..c085bd0 --- /dev/null +++ b/lib/models/chat/combined_last_messages_model_class.dart @@ -0,0 +1,214 @@ +class CombinedMessageModel { + CombinedMessageModel({ + required this.personalMessage, + required this.sortedArrayGroup, + }); + List personalMessage = []; + List sortedArrayGroup = []; + + CombinedMessageModel.empty(); + CombinedMessageModel.fromJson(Map json){ + personalMessage = List.from(json['personalMessage']).map((e)=>PersonalMessage.fromJson(e)).toList(); + sortedArrayGroup = List.from(json['sortedArrayGroup']).map((e)=>SortedArrayGroup.fromJson(e)).toList(); + } + + Map toJson() { + final data = {}; + data['personalMessage'] = personalMessage.map((e)=>e.toJson()).toList(); + data['sortedArrayGroup'] = sortedArrayGroup.map((e)=>e.toJson()).toList(); + return data; + } + + @override + String toString() { + return 'CombinedMessageModel{personalMessage: $personalMessage, sortedArrayGroup: $sortedArrayGroup}'; + } +} + +class PersonalMessage { + PersonalMessage({ + required this.isGroup, + required this.seen, + required this.recieverId, + required this.name, + required this.message, + required this.messageType, + required this.date, + required this.image, + required this.senderId, + }); + bool isGroup = false; + bool seen = false; + String recieverId = ""; + String name = ""; + String message = ""; + String messageType = ""; + String date = ""; //eg. "2024-06-21T09:38:16.352Z" + String image = ""; + String senderId = ""; + + PersonalMessage.empty(); + PersonalMessage.fromJson(Map json){ + isGroup = json['isGroup']??false; + seen = json['seen']??false; + recieverId = json['recieverId']??""; + name = json['name']??""; + message = json['message']??""; + messageType = json['messageType']??""; + date = json['date']??""; + image = json['image']??""; + senderId = json['senderId']??""; + } + + Map toJson() { + final data = {}; + data['isGroup'] = isGroup; + data['seen'] = seen; + data['recieverId'] = recieverId; + data['name'] = name; + data['message'] = message; + data['messageType'] = messageType; + data['date'] = date; + data['image'] = image; + data['senderId'] = senderId; + return data; + } + + @override + String toString() { + return 'PersonalMessage{isGroup: $isGroup, seen: $seen, recieverId: $recieverId, name: $name, message: $message, date: $date, image: $image, senderId: $senderId}'; + } +} + +class SortedArrayGroup { + SortedArrayGroup({ + required this.lastMessages, + required this.groupWorkingScheduleTime, + required this.id, + required this.groupName, + required this.groupImage, + required this.groupMembers, + required this.isGroup, + required this.date, + required this.isActive, + required this.createdAt, + required this.updatedAt, + required this.v, + }); + LastMessages lastMessages = LastMessages.empty(); + GroupWorkingScheduleTime groupWorkingScheduleTime = GroupWorkingScheduleTime.empty(); + String id = ""; + String groupName = ""; + String groupImage = ""; + List groupMembers = []; + bool isGroup = false; + String date = ""; + bool isActive = false; + String createdAt = ""; + String updatedAt = ""; + int v = -1; + + SortedArrayGroup.empty(); + SortedArrayGroup.fromJson(Map json){ + lastMessages = LastMessages.fromJson(json['lastMessages'] ?? LastMessages.empty()); + groupWorkingScheduleTime = GroupWorkingScheduleTime.fromJson(json['groupWorkingScheduleTime'] ?? GroupWorkingScheduleTime.empty()); + id = json['_id'] ?? ""; + groupName = json['groupName'] ?? ""; + groupImage = json['groupImage'] ?? ""; + groupMembers = List.castFrom(json['groupMembers'] ?? []); + isGroup = json['isGroup'] ?? false; + date = json['date'] ?? ""; + isActive = json['isActive'] ?? false; + createdAt = json['createdAt'] ?? ""; + updatedAt = json['updatedAt'] ?? ""; + v = json['__v'] ?? -1; + } + + Map toJson() { + final data = {}; + data['lastMessages'] = lastMessages.toJson(); + data['groupWorkingScheduleTime'] = groupWorkingScheduleTime.toJson(); + data['_id'] = id; + data['groupName'] = groupName; + data['groupImage'] = groupImage; + data['groupMembers'] = groupMembers; + data['isGroup'] = isGroup; + data['date'] = date; + data['isActive'] = isActive; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + data['__v'] = v; + return data; + } + + @override + String toString() { + return 'SortedArrayGroup{lastMessages: $lastMessages, groupWorkingScheduleTime: $groupWorkingScheduleTime, id: $id, groupName: $groupName, groupImage: $groupImage, groupMembers: $groupMembers, isGroup: $isGroup, date: $date, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt, v: $v}'; + } +} + +class LastMessages { + LastMessages({ + required this.message, + required this.messageType, + required this.messageSentBy, + required this.messageTime, + }); + String message = ""; + String messageType = ""; + String messageSentBy = ""; + int messageTime = -1; + + LastMessages.empty(); + LastMessages.fromJson(Map json){ + message = json['message'] ?? ""; + messageType = json['messageType'] ?? ""; + messageSentBy = json['messageSentBy'] ?? ""; + messageTime = json['messageTime'] ?? -1; + } + + Map toJson() { + final data = {}; + data['message'] = message; + data['messageType'] = messageType; + data['messageSentBy'] = messageSentBy; + data['messageTime'] = messageTime; + return data; + } + + @override + String toString() { + return 'LastMessages{message: $message, messageSentBy: $messageSentBy, messageTime: $messageTime}'; + } +} + +class GroupWorkingScheduleTime { + GroupWorkingScheduleTime({ + required this.startTime, + required this.endTime, + required this.totalWorkHours, + }); + int startTime = -1; + int endTime = -1; + String totalWorkHours = ""; + + GroupWorkingScheduleTime.empty(); + GroupWorkingScheduleTime.fromJson(Map json){ + startTime = json['startTime']?? -1; + endTime = json['endTime']?? -1; + totalWorkHours = json['totalWorkHours']?? ""; + } + + Map toJson() { + final data = {}; + data['startTime'] = startTime; + data['endTime'] = endTime; + data['totalWorkHours'] = totalWorkHours; + return data; + } + + @override + String toString() { + return 'GroupWorkingScheduleTime{startTime: $startTime, endTime: $endTime, totalWorkHours: $totalWorkHours}'; + } +} \ No newline at end of file diff --git a/lib/models/chat/single_chat.dart b/lib/models/chat/single_chat.dart new file mode 100644 index 0000000..7076ad2 --- /dev/null +++ b/lib/models/chat/single_chat.dart @@ -0,0 +1,53 @@ +class SingleChatModelClass { + SingleChatModelClass({ + required this.from, + required this.to, + required this.message, + required this.seen, + required this.isDeleted, + required this.isHide, + required this.isPin, + required this.id2, + required this.id, + }); + String from = ""; + String to = ""; + String message = ""; + bool seen = false; + bool isDeleted = false; + bool isHide = false; + bool isPin = false; + String id2 = ""; + String id = ""; + + SingleChatModelClass.fromJson(Map json){ + from = json['from'] ?? ""; + to = json['to'] ?? ""; + message = json['message'] ?? ""; + seen = json['seen'] ?? false; + isDeleted = json['isDeleted'] ?? false; + isHide = json['isHide'] ?? false; + isPin = json['isPin'] ?? false; + id2 = json['_id'] ?? ""; + id = json['id'] ?? ""; + } + + Map toJson() { + final data = {}; + data['from'] = from; + data['to'] = to; + data['message'] = message; + data['seen'] = seen; + data['isDeleted'] = isDeleted; + data['isHide'] = isHide; + data['isPin'] = isPin; + data['_id'] = id2; + data['id'] = id; + return data; + } + + @override + String toString() { + return 'SingleChatModelClass{from: $from, to: $to, message: $message, seen: $seen, isDeleted: $isDeleted, isHide: $isHide, isPin: $isPin, id2: $id2, id: $id}'; + } +} \ No newline at end of file diff --git a/lib/models/chat/update_delete_single_message_model.dart b/lib/models/chat/update_delete_single_message_model.dart new file mode 100644 index 0000000..4145a26 --- /dev/null +++ b/lib/models/chat/update_delete_single_message_model.dart @@ -0,0 +1,69 @@ +class UpdateDeleteSingleMessageModel { + UpdateDeleteSingleMessageModel({ + required this.idOne, + required this.from, + required this.to, + required this.message, + required this.seen, + required this.isDeleted, + required this.isHide, + required this.isPin, + required this.createdAt, + required this.updatedAt, + required this.v, + required this.seenAt, + required this.id, + }); + String idOne = ""; + String from = ""; + String to = ""; + String message = ""; + bool seen = false; + bool isDeleted = false; + bool isHide = false; + bool isPin = false; + String createdAt = ""; + String updatedAt = ""; + int v = -1; + String seenAt = ""; + String id = ""; + + UpdateDeleteSingleMessageModel.fromJson(Map json){ + idOne = json['_id']?? ""; + from = json['from']?? ""; + to = json['to']?? ""; + message = json['message']?? ""; + seen = json['seen']?? false; + isDeleted = json['isDeleted']?? false; + isHide = json['isHide']?? false; + isPin = json['isPin']?? false; + createdAt = json['createdAt']?? ""; + updatedAt = json['updatedAt']?? ""; + v = json['__v']?? -1; + seenAt = json['seenAt']?? ""; + id = json['id']?? ""; + } + + Map toJson() { + final data = {}; + data['_id'] = idOne; + data['from'] = from; + data['to'] = to; + data['message'] = message; + data['seen'] = seen; + data['isDeleted'] = isDeleted; + data['isHide'] = isHide; + data['isPin'] = isPin; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + data['__v'] = v; + data['seenAt'] = seenAt; + data['id'] = id; + return data; + } + + @override + String toString() { + return 'UpdateDeleteSingleMessageModel{idOne: $idOne, from: $from, to: $to, message: $message, seen: $seen, isDeleted: $isDeleted, isHide: $isHide, isPin: $isPin, createdAt: $createdAt, updatedAt: $updatedAt, v: $v, seenAt: $seenAt, id: $id}'; + } +} \ No newline at end of file diff --git a/lib/models/clients/HealthIssuesDetailsModel.dart b/lib/models/clients/HealthIssuesDetailsModel.dart new file mode 100644 index 0000000..ee3b4cd --- /dev/null +++ b/lib/models/clients/HealthIssuesDetailsModel.dart @@ -0,0 +1,58 @@ +import 'package:ftc_mobile_app/models/clients/body_points_category.dart'; + +class HealthIssueDetailsModel { + String sId = ""; + BodyPointsCategory? bodyPointsCategory; + bool status = false; + String healthNote = ""; + String complaint = ""; + String userId = ""; + String createdAt = ""; + String updatedAt = ""; + int iV = -1; + String id = ""; + + HealthIssueDetailsModel( + {required this.sId, + required this.bodyPointsCategory, + required this.status, + required this.healthNote, + required this.complaint, + required this.userId, + required this.createdAt, + required this.updatedAt, + required this.iV, + required this.id}); + + HealthIssueDetailsModel.empty(); + + HealthIssueDetailsModel.fromJson(Map json) { + sId = json['_id'] ?? ""; + bodyPointsCategory = json['category'] != null && json['category'] is Map + ? BodyPointsCategory.fromJson(json['category'] ?? {} ) + : null; + status = json['status']??''=="1"; + healthNote = json['healthNote'] ?? ""; + complaint = json['complaint'] ?? ""; + userId = json['userId'] ?? ""; + createdAt = json['createdAt'] ?? ""; + updatedAt = json['updatedAt'] ?? ""; + iV = json['__v'] ?? ""; + id = json['id'] ?? ""; + } + + Map toJson() { + final Map data = {}; + data['_id'] = sId; + data['category'] = bodyPointsCategory?.toJson(); + data['status'] = status; + data['healthNote'] = healthNote; + data['complaint'] = complaint; + data['userId'] = userId; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + data['__v'] = iV; + data['id'] = id; + return data; + } +} \ No newline at end of file diff --git a/lib/models/clients/PBSPlanModel.dart b/lib/models/clients/PBSPlanModel.dart new file mode 100644 index 0000000..3885c06 --- /dev/null +++ b/lib/models/clients/PBSPlanModel.dart @@ -0,0 +1,381 @@ +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:quill_html_editor/quill_html_editor.dart'; + +class PBSListDataJson { + PBSListDataJson({ + required this.pbsList, + required this.count, + }); + + List pbsList = []; + int count = -1; + + PBSListDataJson.empty(); + + PBSListDataJson.fromJson(Map json) { + for (var item in json['pbsList']) { + pbsList.add(PbsList.fromJson(item)); + } + // pbsList = List.from(json['pbsList']).map((e)=>PbsList.fromJson(e)).toList(); + count = json['count']; + } + + Map toJson() { + final data = {}; + data['pbsList'] = pbsList.map((e) => e.toJson()).toList(); + data['count'] = count; + return data; + } + + @override + String toString() { + return 'PBSListDataJson{pbsList: $pbsList, count: $count}'; + } +} + +class PbsList { + PbsList({ + required this.id, + required this.userIdModelInPbs, + required this.staffId, + required this.aboutPlan, + required this.managementOfBehaviorPlan, + required this.secondaryPrevention, + required this.reactiveStrategies, + required this.postIncidentSupport, + required this.updatedAt, + required this.createdAt, + }); + + String id = ""; + UserData? userIdModelInPbs; + UserData? staffId; + String aboutPlan = ""; + String managementOfBehaviorPlan = ""; + String secondaryPrevention = ""; + String reactiveStrategies = ""; + String postIncidentSupport = ""; + String updatedAt = ""; + String createdAt = ""; + + QuillEditorController aboutPlanQuillController = QuillEditorController(); + QuillEditorController managementOfBehaviouralPresentationQuillController = QuillEditorController(); + QuillEditorController secondaryPreventionQuillController = QuillEditorController(); + QuillEditorController reactiveStrategiesQuillController = QuillEditorController(); + QuillEditorController postIncidentSupportRecoveryQuillController = QuillEditorController(); + + PbsList.empty(); + + PbsList.addData({ + required this.id, + required this.aboutPlan, + required this.managementOfBehaviorPlan, + required this.secondaryPrevention, + required this.reactiveStrategies, + required this.postIncidentSupport, + }); + + PbsList.fromJson(Map json) { + id = json['_id'] ?? ""; + userIdModelInPbs = UserData.fromJson(json['userId'] ?? {}); + staffId = UserData.fromJson(json['staffId'] ?? {}); + aboutPlan = json['aboutPlan'] ?? ""; + managementOfBehaviorPlan = json['managementOfBehaviorPlan'] ?? ""; + secondaryPrevention = json['secondaryPrevention'] ?? ""; + reactiveStrategies = json['reactiveStartegies'] ?? ""; + postIncidentSupport = json['postIncidentSupport'] ?? ""; + updatedAt = json['updatedAt'] ?? ""; + createdAt = json['createdAt'] ?? ""; + } + + Map toJson() { + final data = {}; + data['_id'] = id; + data['userId'] = userIdModelInPbs?.toJson(); + data['staffId'] = staffId?.toJson(); + data['aboutPlan'] = aboutPlan; + data['managementOfBehaviorPlan'] = managementOfBehaviorPlan; + data['secondaryPrevention'] = secondaryPrevention; + data['reactiveStartegies'] = reactiveStrategies; + data['postIncidentSupport'] = postIncidentSupport; + data['updatedAt'] = updatedAt; + data['createdAt'] = createdAt; + return data; + } +} + +// class UserIdModelInPbs { +// UserIdModelInPbs({ +// required this.fcmTokens, +// required this.location, +// required this.id, +// required this.userModelName, +// required this.name, +// required this.version, +// required this.email, +// required this.phoneNumber, +// required this.active, +// required this.role, +// required this.profilePictureUrl, +// required this.deviceId, +// required this.verificationCode, +// required this.isVerified, +// required this.approved, +// required this.blocked, +// required this.createdAt, +// required this.updatedAt, +// required this.v, +// required this.password, +// required this.userSettings, +// required this.modelId, +// }); +// +// FcmTokens fcmTokens = FcmTokens.empty(); +// Location location = Location.empty(); +// String id = ""; +// String userModelName = ""; +// String name = ""; +// String version = ""; +// String email = ""; +// String phoneNumber = ""; +// bool active = false; +// String role = ""; +// String profilePictureUrl = ""; +// String deviceId = ""; +// String verificationCode = ""; +// bool isVerified = false; +// bool approved = false; +// bool blocked = false; +// String createdAt = ""; +// String updatedAt = ""; +// int v = -1; +// String password = ""; +// String userSettings = ""; +// String modelId = ""; +// +// UserIdModelInPbs.empty(); +// +// UserIdModelInPbs.fromJson(Map json) { +// fcmTokens = FcmTokens.fromJson(json['fcm_tokens'] ?? ""); +// location = Location.fromJson(json['location'] ?? ""); +// id = json['_id'] ?? ""; +// userModelName = json['userModelName'] ?? ""; +// name = json['name'] ?? ""; +// version = json['version'] ?? ""; +// email = json['email'] ?? ""; +// phoneNumber = json['phoneNumber'] ?? ""; +// active = json['active'] ?? ""; +// role = json['role'] ?? ""; +// profilePictureUrl = json['profile_picture_url'] ?? ""; +// deviceId = json['deviceId'] ?? ""; +// verificationCode = json['verification_code'] ?? ""; +// isVerified = json['is_verified'] ?? ""; +// approved = json['approved'] ?? ""; +// blocked = json['blocked'] ?? ""; +// createdAt = json['createdAt'] ?? ""; +// updatedAt = json['updatedAt'] ?? ""; +// v = json['__v'] ?? ""; +// password = json['password'] ?? ""; +// userSettings = json['userSettings'] ?? ""; +// modelId = json['modelId'] ?? ""; +// } +// +// Map toJson() { +// final data = {}; +// data['fcm_tokens'] = fcmTokens.toJson(); +// data['location'] = location.toJson(); +// data['_id'] = id; +// data['userModelName'] = userModelName; +// data['name'] = name; +// data['version'] = version; +// data['email'] = email; +// data['phoneNumber'] = phoneNumber; +// data['active'] = active; +// data['role'] = role; +// data['profile_picture_url'] = profilePictureUrl; +// data['deviceId'] = deviceId; +// data['verification_code'] = verificationCode; +// data['is_verified'] = isVerified; +// data['approved'] = approved; +// data['blocked'] = blocked; +// data['createdAt'] = createdAt; +// data['updatedAt'] = updatedAt; +// data['__v'] = v; +// data['password'] = password; +// data['userSettings'] = userSettings; +// data['modelId'] = modelId; +// return data; +// } +// +// @override +// String toString() { +// return 'UserId{fcmTokens: $fcmTokens, location: $location, id: $id, userModelName: $userModelName, name: $name, version: $version, email: $email, phoneNumber: $phoneNumber, active: $active, role: $role, profilePictureUrl: $profilePictureUrl, deviceId: $deviceId, verificationCode: $verificationCode, isVerified: $isVerified, approved: $approved, blocked: $blocked, createdAt: $createdAt, updatedAt: $updatedAt, v: $v, password: $password, userSettings: $userSettings, modelId: $modelId}'; +// } +// } +// +// class FcmTokens { +// FcmTokens({ +// required this.token, +// required this.deviceType, +// }); +// +// String token = ""; +// String deviceType = ""; +// +// FcmTokens.empty(); +// +// FcmTokens.fromJson(Map json) { +// token = json['token'] ?? ""; +// deviceType = json['deviceType'] ?? ""; +// } +// +// Map toJson() { +// final data = {}; +// data['token'] = token; +// data['deviceType'] = deviceType; +// return data; +// } +// +// @override +// String toString() { +// return 'FcmTokens{token: $token, deviceType: $deviceType}'; +// } +// } +// +// class Location { +// Location({ +// required this.type, +// required this.coordinates, +// }); +// +// String type = ""; +// List coordinates = [0, 0]; +// +// Location.empty(); +// +// Location.fromJson(Map json) { +// type = json['type']; +// coordinates = List.castFrom(json['coordinates']); +// } +// +// Map toJson() { +// final data = {}; +// data['type'] = type; +// data['coordinates'] = coordinates; +// return data; +// } +// +// @override +// String toString() { +// return 'Location{type: $type, coordinates: $coordinates}'; +// } +// } +// +// class StaffId { +// StaffId({ +// required this.fcmTokens, +// required this.location, +// required this.id, +// required this.userModelName, +// required this.name, +// required this.version, +// required this.email, +// required this.phoneNumber, +// required this.active, +// required this.role, +// required this.profilePictureUrl, +// required this.deviceId, +// required this.verificationCode, +// required this.isVerified, +// required this.approved, +// required this.blocked, +// required this.createdAt, +// required this.updatedAt, +// required this.v, +// required this.password, +// required this.userSettings, +// required this.modelId, +// }); +// +// FcmTokens fcmTokens = FcmTokens.empty(); +// Location location = Location.empty(); +// String id = ""; +// String userModelName = ""; +// String name = ""; +// String version = ""; +// String email = ""; +// String phoneNumber = ""; +// bool active = false; +// String role = ""; +// String profilePictureUrl = ""; +// String deviceId = ""; +// String verificationCode = ""; +// bool isVerified = false; +// bool approved = false; +// bool blocked = false; +// String createdAt = ""; +// String updatedAt = ""; +// int v = -1; +// String password = ""; +// String userSettings = ""; +// String modelId = ""; +// +// StaffId.empty(); +// +// StaffId.fromJson(Map json) { +// fcmTokens = FcmTokens.fromJson(json['fcm_tokens'] ?? ""); +// location = Location.fromJson(json['location'] ?? ""); +// id = json['_id'] ?? ""; +// userModelName = json['userModelName'] ?? ""; +// name = json['name'] ?? ""; +// version = json['version'] ?? ""; +// email = json['email'] ?? ""; +// phoneNumber = json['phoneNumber'] ?? ""; +// active = json['active'] ?? ""; +// role = json['role'] ?? ""; +// profilePictureUrl = json['profile_picture_url'] ?? ""; +// deviceId = json['deviceId'] ?? ""; +// verificationCode = json['verification_code'] ?? ""; +// isVerified = json['is_verified'] ?? ""; +// approved = json['approved'] ?? ""; +// blocked = json['blocked'] ?? ""; +// createdAt = json['createdAt'] ?? ""; +// updatedAt = json['updatedAt'] ?? ""; +// v = json['__v'] ?? ""; +// password = json['password'] ?? ""; +// userSettings = json['userSettings'] ?? ""; +// modelId = json['modelId'] ?? ""; +// } +// +// Map toJson() { +// final data = {}; +// data['fcm_tokens'] = fcmTokens.toJson(); +// data['location'] = location.toJson(); +// data['_id'] = id; +// data['userModelName'] = userModelName; +// data['name'] = name; +// data['version'] = version; +// data['email'] = email; +// data['phoneNumber'] = phoneNumber; +// data['active'] = active; +// data['role'] = role; +// data['profile_picture_url'] = profilePictureUrl; +// data['deviceId'] = deviceId; +// data['verification_code'] = verificationCode; +// data['is_verified'] = isVerified; +// data['approved'] = approved; +// data['blocked'] = blocked; +// data['createdAt'] = createdAt; +// data['updatedAt'] = updatedAt; +// data['__v'] = v; +// data['password'] = password; +// data['userSettings'] = userSettings; +// data['modelId'] = modelId; +// return data; +// } +// +// @override +// String toString() { +// return 'StaffId{fcmTokens: $fcmTokens, location: $location, id: $id, userModelName: $userModelName, name: $name, version: $version, email: $email, phoneNumber: $phoneNumber, active: $active, role: $role, profilePictureUrl: $profilePictureUrl, deviceId: $deviceId, verificationCode: $verificationCode, isVerified: $isVerified, approved: $approved, blocked: $blocked, createdAt: $createdAt, updatedAt: $updatedAt, v: $v, password: $password, userSettings: $userSettings, modelId: $modelId}'; +// } +// } diff --git a/lib/models/clients/add_pbs_plan_model.dart b/lib/models/clients/add_pbs_plan_model.dart new file mode 100644 index 0000000..98bebf2 --- /dev/null +++ b/lib/models/clients/add_pbs_plan_model.dart @@ -0,0 +1,74 @@ +import 'package:quill_html_editor/quill_html_editor.dart'; + +class AddPBSPlanModel{ + // String incidentTitle = ""; + // String incidentId = ""; + // String userId = ""; + // int incidentDate = 0; + // bool active = false; + // String createdAt = ""; + // String updatedAt = ""; + // int v = 0; + String userId = ""; + String staffId = ""; + String planId = ""; + String aboutPlanNote = ""; + QuillEditorController aboutPlanQuillController = QuillEditorController(); + String managementOfBehaviouralPresentationNote = ""; + QuillEditorController managementOfBehaviouralPresentationQuillController = QuillEditorController(); + String secondaryPreventionNote = ""; + QuillEditorController secondaryPreventionQuillController = QuillEditorController(); + String reactiveStrategiesNote = ""; + QuillEditorController reactiveStrategiesQuillController = QuillEditorController(); + String postIncidentSupportRecoveryNote = ""; + QuillEditorController postIncidentSupportRecoveryQuillController = QuillEditorController(); + + Future get areAllFieldsEdited async { + String aboutPlanText = await aboutPlanQuillController.getText(); + String managementOfBehaviouralPresentationText = + await managementOfBehaviouralPresentationQuillController.getText(); + String secondaryPreventionText = + await secondaryPreventionQuillController.getText(); + String reactiveStrategiesText = + await reactiveStrategiesQuillController.getText(); + String postIncidentSupportRecoveryText = + await postIncidentSupportRecoveryQuillController.getText(); + return aboutPlanText.isNotEmpty && + managementOfBehaviouralPresentationText.isNotEmpty && + secondaryPreventionText.isNotEmpty && + reactiveStrategiesText.isNotEmpty && + postIncidentSupportRecoveryText.isNotEmpty; + } + + AddPBSPlanModel.empty(); + + AddPBSPlanModel.fromJson(Map json){ + userId = json['userId']; + staffId = json['staffId']; + aboutPlanNote = json['aboutPlan']; + managementOfBehaviouralPresentationNote = json['managementOfBehaviorPlan']; + secondaryPreventionNote = json['secondaryPrevention']; + reactiveStrategiesNote = json['reactiveStartegies']; + postIncidentSupportRecoveryNote = json['postIncidentSupport']; + planId = json['_id']; + } + + @override + String toString() { + return 'AddPBSPlanModel{userId: $userId, staffId: $staffId, planId: $planId, aboutPlanNote: $aboutPlanNote, aboutPlanQuillController: $aboutPlanQuillController, managementOfBehaviouralPresentationNote: $managementOfBehaviouralPresentationNote, managementOfBehaviouralPresentationQuillController: $managementOfBehaviouralPresentationQuillController, secondaryPreventionNote: $secondaryPreventionNote, secondaryPreventionQuillController: $secondaryPreventionQuillController, reactiveStrategiesNote: $reactiveStrategiesNote, reactiveStrategiesQuillController: $reactiveStrategiesQuillController, postIncidentSupportRecoveryNote: $postIncidentSupportRecoveryNote, postIncidentSupportRecoveryQuillController: $postIncidentSupportRecoveryQuillController}'; + } + + // Map toJson() { + // final _data = {}; + // _data['userId'] = userId; + // _data['staffId'] = staffId; + // _data['aboutPlan'] = aboutPlan; + // _data['managementOfBehaviorPlan'] = managementOfBehaviorPlan; + // _data['secondaryPrevention'] = secondaryPrevention; + // _data['reactiveStartegies'] = reactiveStartegies; + // _data['postIncidentSupport'] = postIncidentSupport; + // _data['_id'] = _id; + // return _data; + // } + +} \ No newline at end of file diff --git a/lib/models/clients/allCareNotes/AllCareNotesListResponse.dart b/lib/models/clients/allCareNotes/AllCareNotesListResponse.dart new file mode 100644 index 0000000..7ea0941 --- /dev/null +++ b/lib/models/clients/allCareNotes/AllCareNotesListResponse.dart @@ -0,0 +1,33 @@ +import 'CareNoteData.dart'; + +class AllCareNotesListResponse { + AllCareNotesListResponse({ + this.status, + this.message, + this.data, + }); + + AllCareNotesListResponse.success() { + status = "Success"; + } + + AllCareNotesListResponse.fromJson(dynamic json) { + status = json['status']; + message = json['message']; + data = json['data'] != null ? CareNoteData.fromJson(json['data']) : null; + } + + String? status; + String? message; + CareNoteData? data; + + Map toJson() { + final map = {}; + map['status'] = status; + map['message'] = message; + if (data != null) { + map['data'] = data?.toJson(); + } + return map; + } +} diff --git a/lib/models/clients/allCareNotes/CareNoteData.dart b/lib/models/clients/allCareNotes/CareNoteData.dart new file mode 100644 index 0000000..6d6a844 --- /dev/null +++ b/lib/models/clients/allCareNotes/CareNoteData.dart @@ -0,0 +1,42 @@ +import 'CarePlans.dart'; + +class CareNoteData { + CareNoteData({ + this.carePlans, + this.count, + this.carePlanCount, + this.offset, + this.limit, + }); + + CareNoteData.fromJson(dynamic json) { + if (json['carePlans'] != null) { + carePlans = []; + json['carePlans'].forEach((v) { + carePlans?.add(CarePlan.fromJson(v)); + }); + } + count = json['count']; + carePlanCount = json['carePlanCount']; + offset = json['offset']; + limit = json['limit']; + } + + List? carePlans; + int? count; + int? carePlanCount; + int? offset; + int? limit; + + Map toJson() { + final map = {}; + if (carePlans != null) { + map['carePlans'] = carePlans?.map((v) => v.toJson()).toList(); + } + map['count'] = count; + map['carePlanCount'] = carePlanCount; + map['offset'] = offset; + map['limit'] = limit; + return map; + } +} diff --git a/lib/models/clients/allCareNotes/CarePlans.dart b/lib/models/clients/allCareNotes/CarePlans.dart new file mode 100644 index 0000000..08cd76e --- /dev/null +++ b/lib/models/clients/allCareNotes/CarePlans.dart @@ -0,0 +1,200 @@ +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import '../body_points_category.dart'; + +class CarePlan { + CarePlan({ + this.id, + this.eventDateTime, + this.userId, + this.addedby, + this.noteDetails, + this.active, + this.noteType, + this.title, + this.flag, + this.isHTML, + this.createdAt, + this.updatedAt, + this.healthIssueId, + this.keycontacts, + this.riskAssesments, + }); + + CarePlan.fromJson(dynamic json) { + id = json['_id']; + eventDateTime = json['eventDateTime']; + userId = json['userId'] is Map ? UserData.fromJson(json['userId']) : null; + addedby = json['addedby'] is Map ? UserData.fromJson(json['addedby']) : null; + noteDetails = json['noteDetails']; + active = json['active']; + noteType = json['noteType']; + title = json['title']; + flag = json['flag']; + isHTML = json['isHTML']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + + healthIssueId = json['healthIssueId'] != null + ? HealthIssueId.fromJson(json['healthIssueId']) + : null; + keycontacts = json['keycontacts'] != null + ? List.from(json['keycontacts']) + : null; + riskAssesments = json['riskAssesments'] != null + ? List.from(json['riskAssesments']) + : null; + } + + String? id; + int? eventDateTime; + UserData? userId; + UserData? addedby; + String? noteDetails; + bool? active; + String? noteType; + String? title; + bool? flag; + bool? isHTML; + String? createdAt; + String? updatedAt; + HealthIssueId? healthIssueId; + List? keycontacts; + List? riskAssesments; + + Map toJson() { + final map = {}; + map['_id'] = id; + map['eventDateTime'] = eventDateTime; + if (userId != null) { + map['userId'] = userId?.toJson(); + } + if (addedby != null) { + map['addedby'] = addedby?.toJson(); + } + map['noteDetails'] = noteDetails; + map['active'] = active; + map['noteType'] = noteType; + map['title'] = title; + map['flag'] = flag; + map['isHTML'] = isHTML; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + if (healthIssueId != null) { + map['healthIssueId'] = healthIssueId?.toJson(); + } + if (keycontacts != null) { + map['keycontacts'] = keycontacts; + } + if (riskAssesments != null) { + map['riskAssesments'] = riskAssesments; + } + return map; + } +} + +class HealthIssueId { + HealthIssueId.fromJson(dynamic json) { + id = json['_id']; + category = + json['category'] != null ? SubCat.fromJson(json['category']) : null; + status = json['status']; + healthNote = json['healthNote']; + complaint = json['complaint']; + userId = json['userId']; + isCarePlanData = json['isCarePlanData']; + isPhysicalIntervention = json['isPhysicalIntervention']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + } + + String? id; + SubCat? category; + bool? status; + String? healthNote; + String? complaint; + String? userId; + bool? isCarePlanData; + bool? isPhysicalIntervention; + String? createdAt; + String? updatedAt; + + Map toJson() { + final map = {}; + map['_id'] = id; + if (category != null) { + map['category'] = category?.toJson(); + } + map['status'] = status; + map['healthNote'] = healthNote; + map['complaint'] = complaint; + map['userId'] = userId; + map['isCarePlanData'] = isCarePlanData; + map['isPhysicalIntervention'] = isPhysicalIntervention; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + return map; + } +} + +// class Category { +// Category.fromJson(dynamic json) { +// id = json['_id']; +// name = json['name']; +// enumValue = json['enum']; +// parentCategory = json['parentCategory'] != null ? ParentCategory.fromJson(json['parentCategory']) : null; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// } +// +// String? id; +// String? name; +// String? enumValue; +// ParentCategory? parentCategory; +// String? createdAt; +// String? updatedAt; +// int? v; +// +// Map toJson() { +// final map = {}; +// map['_id'] = id; +// map['name'] = name; +// map['enum'] = enumValue; +// if (parentCategory != null) { +// map['parentCategory'] = parentCategory?.toJson(); +// } +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// return map; +// } +// } +// +// class ParentCategory { +// ParentCategory.fromJson(dynamic json) { +// id = json['_id']; +// name = json['name']; +// enumValue = json['enum']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// } +// +// String? id; +// String? name; +// String? enumValue; +// String? createdAt; +// String? updatedAt; +// int? v; +// +// Map toJson() { +// final map = {}; +// map['_id'] = id; +// map['name'] = name; +// map['enum'] = enumValue; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// return map; +// } +// } diff --git a/lib/models/clients/allClientsList/AllClientsResponse.dart b/lib/models/clients/allClientsList/AllClientsResponse.dart new file mode 100644 index 0000000..f9d5d7e --- /dev/null +++ b/lib/models/clients/allClientsList/AllClientsResponse.dart @@ -0,0 +1,734 @@ +import '../../profileData/user_data.dart'; + +class AllClientsResponse { + AllClientsResponse({ + this.success, + this.status, + this.message, + this.data,}); + + AllClientsResponse.fromJson(dynamic json) { + status = json['status']; + message = json['message']; + data = json['data'] != null ? AllClientsResponseData.fromJson(json['data']) : null; + } + + //Local var + bool? success; + + String? status; + String? message; + AllClientsResponseData? data; + + Map toJson() { + final map = {}; + map['status'] = status; + map['message'] = message; + if (data != null) { + map['data'] = data?.toJson(); + } + return map; + } + +} + +class AllClientsResponseData { + AllClientsResponseData({ + this.users, + this.count, + this.offset, + this.limit,}); + + AllClientsResponseData.fromJson(dynamic json) { + if (json['users'] != null) { + users = []; + json['users'].forEach((v) { + users?.add(UserData.fromJson(v)); + }); + } + count = json['count']; + offset = json['offset']; + limit = json['limit']; + } + List? users; + int? count; + int? offset; + int? limit; + + Map toJson() { + final map = {}; + if (users != null) { + map['users'] = users?.map((v) => v.toJson()).toList(); + } + map['count'] = count; + map['offset'] = offset; + map['limit'] = limit; + return map; + } + +} + +// class Users { +// Users({ +// this.id, +// this.userModelName, +// this.active, +// this.role, +// this.profilePictureUrl, +// this.profileVideoUrl, +// this.deviceId, +// this.verificationCode, +// this.isVerified, +// this.approved, +// this.blocked, +// this.name, +// this.phoneNumber, +// this.email, +// this.createdAt, +// this.updatedAt, +// this.v, +// this.modelId,}); +// +// Users.fromJson(dynamic json) { +// id = json['_id']; +// userModelName = json['userModelName']; +// active = json['active']; +// role = json['role']; +// profilePictureUrl = json['profile_picture_url']; +// profileVideoUrl = json['profile_video_url']; +// deviceId = json['deviceId']; +// verificationCode = json['verification_code']; +// isVerified = json['is_verified']; +// approved = json['approved']; +// blocked = json['blocked']; +// name = json['name']; +// phoneNumber = json['phoneNumber']; +// email = json['email']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// modelId = json['modelId'] != null ? ModelId.fromJson(json['modelId']) : null; +// } +// String? id; +// String? userModelName; +// bool? active; +// String? role; +// String? profilePictureUrl; +// String? profileVideoUrl; +// String? deviceId; +// String? verificationCode; +// bool? isVerified; +// bool? approved; +// bool? blocked; +// String? name; +// String? phoneNumber; +// String? email; +// String? createdAt; +// String? updatedAt; +// int? v; +// ModelId? modelId; +// +// Map toJson() { +// final map = {}; +// map['_id'] = id; +// map['userModelName'] = userModelName; +// map['active'] = active; +// map['role'] = role; +// map['profile_picture_url'] = profilePictureUrl; +// map['profile_video_url'] = profileVideoUrl; +// map['deviceId'] = deviceId; +// map['verification_code'] = verificationCode; +// map['is_verified'] = isVerified; +// map['approved'] = approved; +// map['blocked'] = blocked; +// map['name'] = name; +// map['phoneNumber'] = phoneNumber; +// map['email'] = email; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// if (modelId != null) { +// map['modelId'] = modelId?.toJson(); +// } +// return map; +// } +// +// } +// +// class ModelId { +// ModelId({ +// this.id, +// this.suProvider, +// this.currSU, +// this.shifts, +// this.serviceUserMedications, +// this.homeVisitSignOut, +// this.srUsShiftsRequired, +// this.suEnquiries, +// this.active, +// this.diagnosises, +// this.suLastName, +// this.name, +// this.addedby, +// this.user, +// this.createdAt, +// this.updatedAt, +// this.v, +// this.lastModifiedBy, +// this.providerName, +// this.referenceId, +// this.seMedicalAlert, +// this.suAddress1, +// this.suAddress2, +// this.suAge, +// this.suCity, +// this.suDOB, +// this.suEmailHome, +// this.suEmailWork, +// this.suEmergencyContact, +// this.suFamilyHead, +// this.suFirstMiddleName, +// this.suFirstVisitDate, +// this.suHomePhone, +// this.suLastVisitDate, +// this.suMobileHomeNo, +// this.suMobileWorkNo, +// this.suNote, +// this.suPrefHomeNo, +// this.suPrefWorkNo, +// this.suPreferredName, +// this.suReferredBY, +// this.suSex, +// this.suSsn, +// this.suState, +// this.suWorkPhone, +// this.suZip,}); +// +// ModelId.fromJson(dynamic json) { +// id = json['_id']; +// if (json['suProvider'] != null) { +// suProvider = []; +// json['suProvider'].forEach((v) { +// suProvider?.add(SuProvider.fromJson(v)); +// }); +// } +// currSU = json['currSU']; +// if (json['shifts'] != null) { +// shifts = []; +// json['shifts'].forEach((v) { +// shifts?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['serviceUserMedications'] != null) { +// serviceUserMedications = []; +// json['serviceUserMedications'].forEach((v) { +// serviceUserMedications?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['homeVisitSignOut'] != null) { +// homeVisitSignOut = []; +// json['homeVisitSignOut'].forEach((v) { +// homeVisitSignOut?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['srUsShiftsRequired'] != null) { +// srUsShiftsRequired = []; +// json['srUsShiftsRequired'].forEach((v) { +// srUsShiftsRequired?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['suEnquiries'] != null) { +// suEnquiries = []; +// json['suEnquiries'].forEach((v) { +// suEnquiries?.add(SuEnquiries.fromJson(v)); +// }); +// } +// active = json['active']; +// if (json['diagnosises'] != null) { +// diagnosises = []; +// json['diagnosises'].forEach((v) { +// diagnosises?.add(Dynamic.fromJson(v)); +// }); +// } +// suLastName = json['suLastName']; +// name = json['name']; +// addedby = json['addedby']; +// user = json['user']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// lastModifiedBy = json['lastModifiedBy']; +// providerName = json['providerName']; +// referenceId = json['referenceId']; +// seMedicalAlert = json['seMedicalAlert']; +// suAddress1 = json['suAddress1']; +// suAddress2 = json['suAddress2']; +// suAge = json['suAge']; +// suCity = json['suCity']; +// suDOB = json['suDOB']; +// suEmailHome = json['suEmailHome']; +// suEmailWork = json['suEmailWork']; +// suEmergencyContact = json['suEmergencyContact']; +// suFamilyHead = json['suFamilyHead']; +// suFirstMiddleName = json['suFirstMiddleName']; +// suFirstVisitDate = json['suFirstVisitDate']; +// suHomePhone = json['suHomePhone']; +// suLastVisitDate = json['suLastVisitDate']; +// suMobileHomeNo = json['suMobileHomeNo']; +// suMobileWorkNo = json['suMobileWorkNo']; +// suNote = json['suNote']; +// suPrefHomeNo = json['suPrefHomeNo']; +// suPrefWorkNo = json['suPrefWorkNo']; +// suPreferredName = json['suPreferredName']; +// suReferredBY = json['suReferredBY']; +// suSex = json['suSex']; +// suSsn = json['suSsn']; +// suState = json['suState']; +// suWorkPhone = json['suWorkPhone']; +// suZip = json['suZip']; +// } +// String? id; +// List? suProvider; +// bool? currSU; +// List? shifts; +// List? serviceUserMedications; +// List? homeVisitSignOut; +// List? srUsShiftsRequired; +// List? suEnquiries; +// bool? active; +// List? diagnosises; +// String? suLastName; +// String? name; +// String? addedby; +// String? user; +// String? createdAt; +// String? updatedAt; +// int? v; +// String? lastModifiedBy; +// String? providerName; +// String? referenceId; +// String? seMedicalAlert; +// String? suAddress1; +// String? suAddress2; +// String? suAge; +// String? suCity; +// String? suDOB; +// String? suEmailHome; +// String? suEmailWork; +// String? suEmergencyContact; +// String? suFamilyHead; +// String? suFirstMiddleName; +// String? suFirstVisitDate; +// String? suHomePhone; +// String? suLastVisitDate; +// String? suMobileHomeNo; +// String? suMobileWorkNo; +// String? suNote; +// String? suPrefHomeNo; +// String? suPrefWorkNo; +// String? suPreferredName; +// String? suReferredBY; +// String? suSex; +// String? suSsn; +// String? suState; +// String? suWorkPhone; +// String? suZip; +// +// Map toJson() { +// final map = {}; +// map['_id'] = id; +// if (suProvider != null) { +// map['suProvider'] = suProvider?.map((v) => v.toJson()).toList(); +// } +// map['currSU'] = currSU; +// if (shifts != null) { +// map['shifts'] = shifts?.map((v) => v.toJson()).toList(); +// } +// if (serviceUserMedications != null) { +// map['serviceUserMedications'] = serviceUserMedications?.map((v) => v.toJson()).toList(); +// } +// if (homeVisitSignOut != null) { +// map['homeVisitSignOut'] = homeVisitSignOut?.map((v) => v.toJson()).toList(); +// } +// if (srUsShiftsRequired != null) { +// map['srUsShiftsRequired'] = srUsShiftsRequired?.map((v) => v.toJson()).toList(); +// } +// if (suEnquiries != null) { +// map['suEnquiries'] = suEnquiries?.map((v) => v.toJson()).toList(); +// } +// map['active'] = active; +// if (diagnosises != null) { +// map['diagnosises'] = diagnosises?.map((v) => v.toJson()).toList(); +// } +// map['suLastName'] = suLastName; +// map['name'] = name; +// map['addedby'] = addedby; +// map['user'] = user; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// map['lastModifiedBy'] = lastModifiedBy; +// map['providerName'] = providerName; +// map['referenceId'] = referenceId; +// map['seMedicalAlert'] = seMedicalAlert; +// map['suAddress1'] = suAddress1; +// map['suAddress2'] = suAddress2; +// map['suAge'] = suAge; +// map['suCity'] = suCity; +// map['suDOB'] = suDOB; +// map['suEmailHome'] = suEmailHome; +// map['suEmailWork'] = suEmailWork; +// map['suEmergencyContact'] = suEmergencyContact; +// map['suFamilyHead'] = suFamilyHead; +// map['suFirstMiddleName'] = suFirstMiddleName; +// map['suFirstVisitDate'] = suFirstVisitDate; +// map['suHomePhone'] = suHomePhone; +// map['suLastVisitDate'] = suLastVisitDate; +// map['suMobileHomeNo'] = suMobileHomeNo; +// map['suMobileWorkNo'] = suMobileWorkNo; +// map['suNote'] = suNote; +// map['suPrefHomeNo'] = suPrefHomeNo; +// map['suPrefWorkNo'] = suPrefWorkNo; +// map['suPreferredName'] = suPreferredName; +// map['suReferredBY'] = suReferredBY; +// map['suSex'] = suSex; +// map['suSsn'] = suSsn; +// map['suState'] = suState; +// map['suWorkPhone'] = suWorkPhone; +// map['suZip'] = suZip; +// return map; +// } +// +// } +// +// class SuEnquiries { +// SuEnquiries({ +// this.id, +// this.initialContactDate, +// this.suEnqContactNo, +// this.suEnqEmail, +// this.reasonForNeed, +// this.suEnqStatus, +// this.requestType, +// this.suEnqComments, +// this.serviceUser, +// this.addedby, +// this.active, +// this.referenceId, +// this.createdAt, +// this.updatedAt, +// this.v, +// this.lastModifiedBy,}); +// +// SuEnquiries.fromJson(dynamic json) { +// id = json['_id']; +// initialContactDate = json['initialContactDate']; +// suEnqContactNo = json['suEnqContactNo']; +// suEnqEmail = json['suEnqEmail']; +// reasonForNeed = json['reasonForNeed']; +// suEnqStatus = json['suEnqStatus']; +// requestType = json['requestType']; +// suEnqComments = json['suEnqComments']; +// serviceUser = json['serviceUser']; +// addedby = json['addedby']; +// active = json['active']; +// referenceId = json['referenceId']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// lastModifiedBy = json['lastModifiedBy']; +// } +// String? id; +// String? initialContactDate; +// String? suEnqContactNo; +// String? suEnqEmail; +// String? reasonForNeed; +// String? suEnqStatus; +// String? requestType; +// String? suEnqComments; +// String? serviceUser; +// String? addedby; +// bool? active; +// String? referenceId; +// String? createdAt; +// String? updatedAt; +// int? v; +// String? lastModifiedBy; +// +// Map toJson() { +// final map = {}; +// map['_id'] = id; +// map['initialContactDate'] = initialContactDate; +// map['suEnqContactNo'] = suEnqContactNo; +// map['suEnqEmail'] = suEnqEmail; +// map['reasonForNeed'] = reasonForNeed; +// map['suEnqStatus'] = suEnqStatus; +// map['requestType'] = requestType; +// map['suEnqComments'] = suEnqComments; +// map['serviceUser'] = serviceUser; +// map['addedby'] = addedby; +// map['active'] = active; +// map['referenceId'] = referenceId; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// map['lastModifiedBy'] = lastModifiedBy; +// return map; +// } +// +// } +// +// class SuProvider { +// SuProvider({ +// this.contractedHours, +// this.id, +// this.staffMemberName, +// this.staffDesignation, +// this.staffOnLeave, +// this.supervisorId, +// this.holidays, +// this.complianceDocuments, +// this.driverFields, +// this.niNumber, +// this.kin, +// this.user, +// this.clients, +// this.staffWorkLoads, +// this.staffHolidayRequests, +// this.staffTrainings, +// this.supervision, +// this.underSupervisions, +// this.staffWorkingDays, +// this.stafDob, +// this.active, +// this.covidCheck, +// this.createdAt, +// this.updatedAt, +// this.v, +// this.staffDisciplinaries, +// this.staffReferences,}); +// +// SuProvider.fromJson(dynamic json) { +// contractedHours = json['contractedHours'] != null ? ContractedHours.fromJson(json['contractedHours']) : null; +// id = json['_id']; +// staffMemberName = json['staffMemberName']; +// staffDesignation = json['staffDesignation']; +// staffOnLeave = json['staffOnLeave']; +// supervisorId = json['supervisorId']; +// if (json['holidays'] != null) { +// holidays = []; +// json['holidays'].forEach((v) { +// holidays?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['complianceDocuments'] != null) { +// complianceDocuments = []; +// json['complianceDocuments'].forEach((v) { +// complianceDocuments?.add(Dynamic.fromJson(v)); +// }); +// } +// driverFields = json['driverFields'] != null ? DriverFields.fromJson(json['driverFields']) : null; +// niNumber = json['niNumber']; +// kin = json['kin']; +// user = json['user']; +// if (json['clients'] != null) { +// clients = []; +// json['clients'].forEach((v) { +// clients?.add(Dynamic.fromJson(v)); +// }); +// } +// staffWorkLoads = json['staffWorkLoads'] != null ? json['staffWorkLoads'].cast() : []; +// if (json['staffHolidayRequests'] != null) { +// staffHolidayRequests = []; +// json['staffHolidayRequests'].forEach((v) { +// staffHolidayRequests?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['staffTrainings'] != null) { +// staffTrainings = []; +// json['staffTrainings'].forEach((v) { +// staffTrainings?.add(Dynamic.fromJson(v)); +// }); +// } +// supervision = json['supervision'] != null ? Supervision.fromJson(json['supervision']) : null; +// if (json['underSupervisions'] != null) { +// underSupervisions = []; +// json['underSupervisions'].forEach((v) { +// underSupervisions?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['staffWorkingDays'] != null) { +// staffWorkingDays = []; +// json['staffWorkingDays'].forEach((v) { +// staffWorkingDays?.add(Dynamic.fromJson(v)); +// }); +// } +// stafDob = json['stafDob']; +// active = json['active']; +// covidCheck = json['covidCheck']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// staffDisciplinaries = json['staffDisciplinaries']; +// staffReferences = json['staffReferences']; +// } +// ContractedHours? contractedHours; +// String? id; +// String? staffMemberName; +// String? staffDesignation; +// bool? staffOnLeave; +// String? supervisorId; +// List? holidays; +// List? complianceDocuments; +// DriverFields? driverFields; +// String? niNumber; +// String? kin; +// String? user; +// List? clients; +// List? staffWorkLoads; +// List? staffHolidayRequests; +// List? staffTrainings; +// Supervision? supervision; +// List? underSupervisions; +// List? staffWorkingDays; +// String? stafDob; +// bool? active; +// bool? covidCheck; +// String? createdAt; +// String? updatedAt; +// int? v; +// String? staffDisciplinaries; +// String? staffReferences; +// +// Map toJson() { +// final map = {}; +// if (contractedHours != null) { +// map['contractedHours'] = contractedHours?.toJson(); +// } +// map['_id'] = id; +// map['staffMemberName'] = staffMemberName; +// map['staffDesignation'] = staffDesignation; +// map['staffOnLeave'] = staffOnLeave; +// map['supervisorId'] = supervisorId; +// if (holidays != null) { +// map['holidays'] = holidays?.map((v) => v.toJson()).toList(); +// } +// if (complianceDocuments != null) { +// map['complianceDocuments'] = complianceDocuments?.map((v) => v.toJson()).toList(); +// } +// if (driverFields != null) { +// map['driverFields'] = driverFields?.toJson(); +// } +// map['niNumber'] = niNumber; +// map['kin'] = kin; +// map['user'] = user; +// if (clients != null) { +// map['clients'] = clients?.map((v) => v.toJson()).toList(); +// } +// map['staffWorkLoads'] = staffWorkLoads; +// if (staffHolidayRequests != null) { +// map['staffHolidayRequests'] = staffHolidayRequests?.map((v) => v.toJson()).toList(); +// } +// if (staffTrainings != null) { +// map['staffTrainings'] = staffTrainings?.map((v) => v.toJson()).toList(); +// } +// if (supervision != null) { +// map['supervision'] = supervision?.toJson(); +// } +// if (underSupervisions != null) { +// map['underSupervisions'] = underSupervisions?.map((v) => v.toJson()).toList(); +// } +// if (staffWorkingDays != null) { +// map['staffWorkingDays'] = staffWorkingDays?.map((v) => v.toJson()).toList(); +// } +// map['stafDob'] = stafDob; +// map['active'] = active; +// map['covidCheck'] = covidCheck; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// map['staffDisciplinaries'] = staffDisciplinaries; +// map['staffReferences'] = staffReferences; +// return map; +// } +// +// } +// +// class Supervision { +// Supervision({ +// this.supervisionName, +// this.sprDueDate, +// this.sprStatus, +// this.sprResult, +// this.templateTitleId,}); +// +// Supervision.fromJson(dynamic json) { +// supervisionName = json['supervisionName']; +// sprDueDate = json['sprDueDate']; +// sprStatus = json['sprStatus']; +// sprResult = json['sprResult']; +// templateTitleId = json['templateTitleId']; +// } +// String? supervisionName; +// String? sprDueDate; +// String? sprStatus; +// String? sprResult; +// String? templateTitleId; +// +// Map toJson() { +// final map = {}; +// map['supervisionName'] = supervisionName; +// map['sprDueDate'] = sprDueDate; +// map['sprStatus'] = sprStatus; +// map['sprResult'] = sprResult; +// map['templateTitleId'] = templateTitleId; +// return map; +// } +// +// } +// +// class DriverFields { +// DriverFields({ +// this.isDriver, +// this.vehicleType,}); +// +// DriverFields.fromJson(dynamic json) { +// isDriver = json['isDriver']; +// vehicleType = json['vehicleType']; +// } +// bool? isDriver; +// String? vehicleType; +// +// Map toJson() { +// final map = {}; +// map['isDriver'] = isDriver; +// map['vehicleType'] = vehicleType; +// return map; +// } +// +// } +// +// class ContractedHours { +// ContractedHours({ +// this.contractedHours, +// this.totalShiftHoursWeek, +// this.noOfShifts,}); +// +// ContractedHours.fromJson(dynamic json) { +// contractedHours = json['contractedHours']; +// totalShiftHoursWeek = json['totalShiftHoursWeek']; +// noOfShifts = json['noOfShifts']; +// } +// int? contractedHours; +// int? totalShiftHoursWeek; +// int? noOfShifts; +// +// Map toJson() { +// final map = {}; +// map['contractedHours'] = contractedHours; +// map['totalShiftHoursWeek'] = totalShiftHoursWeek; +// map['noOfShifts'] = noOfShifts; +// return map; +// } +// +// } \ No newline at end of file diff --git a/lib/models/clients/body_points_category.dart b/lib/models/clients/body_points_category.dart new file mode 100644 index 0000000..0be8293 --- /dev/null +++ b/lib/models/clients/body_points_category.dart @@ -0,0 +1,116 @@ +class BodyPointsCategory { + String idOne = ""; + String name = ""; + String enumed = ""; + String createdAt = ""; + String updatedAt = ""; + int v = 0; + List subCategory = []; + String id = ""; + + BodyPointsCategory({ + required this.idOne, + required this.name, + required this.enumed, + required this.createdAt, + required this.updatedAt, + required this.v, + required this.subCategory, + required this.id, + }); + + @override + String toString() { + return 'BodyPointsCategory{idOne: $idOne, name: $name, enumed: $enumed, createdAt: $createdAt, updatedAt: $updatedAt, v: $v, subCategory: $subCategory, id: $id}'; + } + + // // Override == operator and hashCode getter + // @override + // bool operator ==(other) => + // identical(this, other) || + // other is BodyPointsCategory && runtimeType == other.runtimeType && id == other.id; + // + // @override + // int get hashCode => id.hashCode; + + BodyPointsCategory.empty(); + + BodyPointsCategory.fromJson(Map json) { + idOne = json['_id'] ?? ""; + name = json['name'] ?? ""; + enumed = json['enum'] ?? ""; + createdAt = json['createdAt'] ?? ""; + updatedAt = json['updatedAt'] ?? ""; + v = json['__v'] ?? 0; + subCategory = json['subcat'] != null + ? List.from(json['subcat']).map((e) => SubCat.fromJson(e)).toList() + : [SubCat.empty()]; + id = json['id'] ?? ""; + } + + Map toJson() { + final data = {}; + data['_id'] = idOne; + data['name'] = name; + data['enum'] = enumed; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + data['__v'] = v; + data['subcat'] = subCategory.map((e) => e.toJson()).toList(); + data['id'] = id; + return data; + } +} + +class SubCat { + String idOne = ""; + String name = ""; + String enumed = ""; + dynamic parentCategory; + String createdAt = ""; + String updatedAt = ""; + int v = 0; + String id = ""; + + @override + String toString() { + return 'SubCat{idOne: $idOne, name: $name, enumed: $enumed, parentCategory: $parentCategory, createdAt: $createdAt, updatedAt: $updatedAt, v: $v, id: $id}'; + } + + SubCat({ + required this.idOne, + required this.name, + required this.enumed, + required this.parentCategory, + required this.createdAt, + required this.updatedAt, + required this.v, + required this.id, + }); + + SubCat.empty(); + + SubCat.fromJson(Map json) { + idOne = json['_id'] ?? ""; + name = json['name'] ?? ""; + enumed = json['enum'] ?? ""; + parentCategory = json['parentCategory'] is Map + ? BodyPointsCategory.fromJson(json['parentCategory']) + : json['parentCategory']; + createdAt = json['createdAt'] ?? ""; + updatedAt = json['updatedAt'] ?? ""; + id = json['id'] ?? ""; + } + + Map toJson() { + final data = {}; + data['_id'] = idOne; + data['name'] = name; + data['enum'] = enumed; + data['parentCategory'] = parentCategory; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + data['id'] = id; + return data; + } +} diff --git a/lib/models/clients/body_points_manager.dart b/lib/models/clients/body_points_manager.dart new file mode 100644 index 0000000..132ccf0 --- /dev/null +++ b/lib/models/clients/body_points_manager.dart @@ -0,0 +1,50 @@ +import 'package:get/get_rx/src/rx_types/rx_types.dart'; + +class BodyPointsManager { + double? triangleTopPosition; + double? triangleBottomPosition; + double? triangleRightPosition; + double? triangleLeftPosition; + double? dialogTopPosition; + double? dialogBottomPosition; + double? dialogRightPosition; + double? dialogLeftPosition; + String pointName = ""; + String healthNote = ""; + String complaint = ""; + String lastUpdate = ""; + String pointParentId = ""; + String pointId = ""; + String pointIssueId = ""; + RxBool isPointInactive = true.obs; + RxBool pointVisibility = false.obs; + RxString pointStatusSelectedDropdownValue = "Active".obs; + String serviceUserId = ""; + + BodyPointsManager.empty(); + + BodyPointsManager.addPoint({ + required this.pointParentId, + required this.isPointInactive, + required this.pointId, + required this.pointName, + required this.pointVisibility, + required this.pointStatusSelectedDropdownValue, + this.triangleTopPosition, + this.triangleBottomPosition, + this.triangleRightPosition, + this.triangleLeftPosition, + this.dialogTopPosition, + this.dialogBottomPosition, + this.dialogRightPosition, + this.dialogLeftPosition, + required this.healthNote, + required this.complaint, + required this.lastUpdate, + }); + + @override + String toString() { + return 'BodyPointsManager{Point Id $pointId,triangleTopPosition: $triangleTopPosition, triangleBottomPosition: $triangleBottomPosition, triangleRightPosition: $triangleRightPosition, triangleLeftPosition: $triangleLeftPosition, dialogTopPosition: $dialogTopPosition, dialogBottomPosition: $dialogBottomPosition, dialogRightPosition: $dialogRightPosition, dialogLeftPosition: $dialogLeftPosition, pointName: $pointName, healthNote: $healthNote, complaint: $complaint, lastUpdate: $lastUpdate, isPointInactive: $isPointInactive, pointVisibility: $pointVisibility, pointStatusSelectedDropdownValue: $pointStatusSelectedDropdownValue}'; + } +} \ No newline at end of file diff --git a/lib/models/clients/careNoteFormsRequests/ABC_form_html_request.dart b/lib/models/clients/careNoteFormsRequests/ABC_form_html_request.dart new file mode 100644 index 0000000..36213c8 --- /dev/null +++ b/lib/models/clients/careNoteFormsRequests/ABC_form_html_request.dart @@ -0,0 +1,25 @@ +class ABCFormHtmlRequest { + final String antecedentEvents; + final String behaviour; + final String consequenceEvents; + + ABCFormHtmlRequest({ + required this.antecedentEvents, + required this.behaviour, + required this.consequenceEvents, + }); + + String toHtml() { + return """ +
+

Antecedent Events: $antecedentEvents

+
+
+

Behaviour: $behaviour

+
+
+

Consequence Events: $consequenceEvents

+
+ """; + } +} diff --git a/lib/models/clients/careNoteFormsRequests/HtmlTableOption.dart b/lib/models/clients/careNoteFormsRequests/HtmlTableOption.dart new file mode 100644 index 0000000..d2ee0e5 --- /dev/null +++ b/lib/models/clients/careNoteFormsRequests/HtmlTableOption.dart @@ -0,0 +1,12 @@ +import 'package:get/get_rx/get_rx.dart'; + +class HtmlTableOption { + String id; + String requirements; + final RxBool isChecked = false.obs; + + HtmlTableOption({ + required this.id, + required this.requirements, + }); +} diff --git a/lib/models/clients/careNoteFormsRequests/consent_capacity_html_request.dart b/lib/models/clients/careNoteFormsRequests/consent_capacity_html_request.dart new file mode 100644 index 0000000..a30dbe1 --- /dev/null +++ b/lib/models/clients/careNoteFormsRequests/consent_capacity_html_request.dart @@ -0,0 +1,313 @@ +import 'package:html/parser.dart' as htmlparser; +import 'package:html/dom.dart' as dom; +import 'HtmlTableOption.dart'; + +class ConsentCapacityHtmlRequest { + final String comments; + final String isMCARequired; + final String mentalCapacityAssessmentDetail; + final String specificDecisionDetail; + final String personUndertakingName; + final String personUndertakingRole; + final String organisation; + final String address; + final String tel; + final String email; + final String dateAndTimeOfAssessment; + final String lackCapacityToMakeParticularDecisionDetail; + final String recordYourEvidenceDescribe; + final String viableOptionsConsidered; + final String explainWhyTickedBox; + final List canDecisionBeDelayedOptions; + final String selectedImpairmentOption; + final String impairmentDescribe; + final String selectedCanPersonDecisionInfoOption; + final String describeCanPersonDecisionInfo; + final String selectedCanTheyRetainOption; + final String describeCanTheyRetain; + final String selectedCanTheyUseOption; + final String describeCanTheyUse; + final String selectedCanTheyCommunicateOption; + final String describeCanTheyCommunicate; + final List causativeNexusOptions; + final String evidence; + final String selectedDoYouHaveConcernOption; + final String whatIsYourEvidence; + final String seekingManagementDescribe; + final String recordInterviewDescribe; + final String section9DontHaveDecisionNameData; + final String section9DontHaveDecisionDateData; + final String section9HaveDecisionNameData; + final String section9HaveDecisionDateData; + final String isIMCARequired; + final String giveWhyIMCARequiredReason; + final String whyIMCARequiredDateTime; + final String section9AssessorsName; + final String assessorsDesignation; + final String assessorsBaseAddress; + final String assessorsContactDetailsData; + + ConsentCapacityHtmlRequest({ + required this.comments, + required this.isMCARequired, + required this.mentalCapacityAssessmentDetail, + required this.specificDecisionDetail, + required this.personUndertakingName, + required this.personUndertakingRole, + required this.organisation, + required this.address, + required this.tel, + required this.email, + required this.dateAndTimeOfAssessment, + required this.lackCapacityToMakeParticularDecisionDetail, + required this.recordYourEvidenceDescribe, + required this.viableOptionsConsidered, + required this.explainWhyTickedBox, + required this.canDecisionBeDelayedOptions, + required this.selectedImpairmentOption, + required this.impairmentDescribe, + required this.selectedCanPersonDecisionInfoOption, + required this.describeCanPersonDecisionInfo, + required this.selectedCanTheyRetainOption, + required this.describeCanTheyRetain, + required this.selectedCanTheyUseOption, + required this.describeCanTheyUse, + required this.selectedCanTheyCommunicateOption, + required this.describeCanTheyCommunicate, + required this.causativeNexusOptions, + required this.evidence, + required this.selectedDoYouHaveConcernOption, + required this.whatIsYourEvidence, + required this.seekingManagementDescribe, + required this.recordInterviewDescribe, + required this.section9DontHaveDecisionNameData, + required this.section9DontHaveDecisionDateData, + required this.section9HaveDecisionNameData, + required this.section9HaveDecisionDateData, + required this.isIMCARequired, + required this.giveWhyIMCARequiredReason, + required this.whyIMCARequiredDateTime, + required this.section9AssessorsName, + required this.assessorsDesignation, + required this.assessorsBaseAddress, + required this.assessorsContactDetailsData, + }); + + String toHtml(String htmlString) { + // Process the HTML string as needed + dom.Document document = htmlparser.parse(htmlString); + + _addTextToElementId( + id: 'isMCARequiredData', value: isMCARequired, document: document); + _addTextToElementId( + id: 'commentsData', value: comments, document: document); + + final myElement = document.getElementById("ifMcaRequiredContentDiv"); + if (isMCARequired == "No") { + myElement?.attributes['style'] = 'display: none;'; + } else { + myElement?.attributes['style'] = 'display: block;'; + + _addTextToElementId( + id: 'mentalCapacityAssessmentDetail', + value: mentalCapacityAssessmentDetail, + document: document); + + _addTextToElementId( + id: 'specificDecisionDetail', + value: specificDecisionDetail, + document: document); + + _addTextToElementId( + id: 'personUndertakingName', + value: personUndertakingName, + document: document); + + _addTextToElementId( + id: 'personUndertakingRole', + value: personUndertakingRole, + document: document); + + _addTextToElementId( + id: 'organisation', value: organisation, document: document); + + _addTextToElementId(id: 'address', value: address, document: document); + _addTextToElementId(id: 'tel', value: tel, document: document); + _addTextToElementId(id: 'email', value: email, document: document); + _addTextToElementId( + id: 'dateAndTimeOfAssessment', + value: dateAndTimeOfAssessment, + document: document); + _addTextToElementId( + id: 'lackCapacityToMakeParticularDecisionDetail', + value: lackCapacityToMakeParticularDecisionDetail, + document: document); + _addTextToElementId( + id: 'recordYourEvidenceDescribe', + value: recordYourEvidenceDescribe, + document: document); + _addTextToElementId( + id: 'viableOptionsConsidered', + value: viableOptionsConsidered, + document: document); + + //canDecisionBeDelayedTable + final String generatedHtml1 = + generateHtmlRows(canDecisionBeDelayedOptions); + print("table1: $generatedHtml1"); + final tableBody1 = document.getElementById('canDecisionBeDelayedTable'); + // document.querySelector('.canDecisionBeDelayedTable tbody'); + print("is tableBody1 null: ${tableBody1 == null}"); + tableBody1?.innerHtml = generatedHtml1; + + _addTextToElementId( + id: 'explainWhyTickedBox', + value: explainWhyTickedBox, + document: document); + + _addTextToElementId( + id: 'selectedImpairmentOption', + value: selectedImpairmentOption, + document: document); + + _addTextToElementId( + id: 'impairmentDescribe', + value: impairmentDescribe, + document: document); + + _addTextToElementId( + id: 'selectedCanPersonDecisionInfoOption', + value: selectedCanPersonDecisionInfoOption, + document: document); + _addTextToElementId( + id: 'describeCanPersonDecisionInfo', + value: describeCanPersonDecisionInfo, + document: document); + _addTextToElementId( + id: 'selectedCanTheyRetainOption', + value: selectedCanTheyRetainOption, + document: document); + _addTextToElementId( + id: 'describeCanTheyRetain', + value: describeCanTheyRetain, + document: document); + _addTextToElementId( + id: 'selectedCanTheyUseOption', + value: selectedCanTheyUseOption, + document: document); + _addTextToElementId( + id: 'describeCanTheyUse', + value: describeCanTheyUse, + document: document); + _addTextToElementId( + id: 'selectedCanTheyCommunicateOption', + value: selectedCanTheyCommunicateOption, + document: document); + _addTextToElementId( + id: 'describeCanTheyCommunicate', + value: describeCanTheyCommunicate, + document: document); + + final String generatedHtml2 = generateHtmlRows(causativeNexusOptions); + print("table2: $generatedHtml2"); + final tableBody2 = document.getElementById('causativeNexusOptions'); + // final tableBody2 = document.querySelector('.causativeNexusOptions tbody'); + print("is table2 null: ${tableBody2 == null}"); + tableBody2?.innerHtml = generatedHtml2; + + _addTextToElementId(id: 'evidence', value: evidence, document: document); + _addTextToElementId( + id: 'selectedDoYouHaveConcernOption', + value: selectedDoYouHaveConcernOption, + document: document); + _addTextToElementId( + id: 'whatIsYourEvidence', + value: whatIsYourEvidence, + document: document); + _addTextToElementId( + id: 'seekingManagementDescribe', + value: seekingManagementDescribe, + document: document); + _addTextToElementId( + id: 'recordInterviewDescribe', + value: recordInterviewDescribe, + document: document); + _addTextToElementId( + id: 'section9DontHaveDecisionNameData', + value: section9DontHaveDecisionNameData, + document: document); + _addTextToElementId( + id: 'section9DontHaveDecisionDateData', + value: section9DontHaveDecisionDateData, + document: document); + _addTextToElementId( + id: 'section9HaveDecisionNameData', + value: section9HaveDecisionNameData, + document: document); + _addTextToElementId( + id: 'section9HaveDecisionDateData', + value: section9HaveDecisionDateData, + document: document); + _addTextToElementId( + id: 'isIMCARequired', value: isIMCARequired, document: document); + _addTextToElementId( + id: 'giveWhyIMCARequiredReason', + value: giveWhyIMCARequiredReason, + document: document); + _addTextToElementId( + id: 'whyIMCARequiredDateTime', + value: whyIMCARequiredDateTime, + document: document); + _addTextToElementId( + id: 'section9AssessorsName', + value: section9AssessorsName, + document: document); + _addTextToElementId( + id: 'assessorsDesignation', + value: assessorsDesignation, + document: document); + _addTextToElementId( + id: 'assessorsBaseAddress', + value: assessorsBaseAddress, + document: document); + _addTextToElementId( + id: 'assessorsContactDetailsData', + value: assessorsContactDetailsData, + document: document); + } + + return document.outerHtml; + } + + _addTextToElementId({ + required String id, + required String value, + required dom.Document document, + }) { + final myElement = document.getElementById(id); + if (myElement != null) { + myElement.text = value; + } + } + + String generateHtmlRows(List options) { + final StringBuffer htmlBuffer = StringBuffer(); + + for (final option in options) { + final String rowHtml = ''' + ${option.requirements} + + ${option.isChecked.value ? 'Tick' : 'Close'} + + + '''; + + htmlBuffer.write(rowHtml); + } + + return htmlBuffer.toString(); + } +} diff --git a/lib/models/clients/careNoteFormsRequests/health_appointments_form_html_request.dart b/lib/models/clients/careNoteFormsRequests/health_appointments_form_html_request.dart new file mode 100644 index 0000000..4ae81f1 --- /dev/null +++ b/lib/models/clients/careNoteFormsRequests/health_appointments_form_html_request.dart @@ -0,0 +1,23 @@ +class HealthAppointmentsFormHtmlRequest { + final String appointmentWith; + final String reason; + final String comments; + + HealthAppointmentsFormHtmlRequest({ + required this.appointmentWith, + required this.reason, + required this.comments, + }); + + String toHtml() { + return """
+

Appointment with: $appointmentWith

+
+
+

Reason for appointment: $reason

+
+
+

Comments: $comments

+
"""; + } +} diff --git a/lib/models/clients/careNoteFormsRequests/nutrition_hydration_form_html_request.dart b/lib/models/clients/careNoteFormsRequests/nutrition_hydration_form_html_request.dart new file mode 100644 index 0000000..54329f0 --- /dev/null +++ b/lib/models/clients/careNoteFormsRequests/nutrition_hydration_form_html_request.dart @@ -0,0 +1,35 @@ +class NutritionHydrationFormHtmlRequest { + final String nutritionHydrationType; + final String foodFluidType; + final String foodFluidAmount; + final String comments; + + NutritionHydrationFormHtmlRequest({ + required this.nutritionHydrationType, + required this.foodFluidType, + required this.foodFluidAmount, + required this.comments, + }); + + String toHtml() { + return """
+

Type: $nutritionHydrationType +

+
+
+

${nutritionHydrationType == "Food" ? "Meal Type: (Breakfast, Lunch, Dinner, Snack etc)" : nutritionHydrationType == "Fluid" ? "Drink Type" : ""}: $foodFluidType

+
+
+

${nutritionHydrationType == "Food" ? "Amount Eaten" : nutritionHydrationType == "Fluid" ? "Amount (ML)" : ""}: $foodFluidAmount

+
+
+

Comments: ${comments.isNotEmpty ? comments : "No assistance required"} +

+
+
+ ${(nutritionHydrationType == "Food") ? """Plat""" : nutritionHydrationType == "Fluid" ? """Glass""" : ""}
+ """; + } +} diff --git a/lib/models/clients/careNoteFormsRequests/observations_form_html_request.dart b/lib/models/clients/careNoteFormsRequests/observations_form_html_request.dart new file mode 100644 index 0000000..013110c --- /dev/null +++ b/lib/models/clients/careNoteFormsRequests/observations_form_html_request.dart @@ -0,0 +1,48 @@ +class ObservationsFormHtmlReq { + final String heartRate; + final String bloodPressure; + final String respiratoryRate; + final String oxygen; + final String temperature; + final String bloodSugar; + + ObservationsFormHtmlReq({ + required this.heartRate, + required this.bloodPressure, + required this.respiratoryRate, + required this.oxygen, + required this.temperature, + required this.bloodSugar, + }); + + String toHtml() { + return """
+

Heart Rate (BPM): $heartRate

+
+
+

Blood Pressure (/MMHG): $bloodPressure +

+
+
+

Respiratory Rate: $respiratoryRate +

+
+
+

Oxygen (%): $oxygen +

+
+
+

Temperature (˚C): $temperature +

+
+
+

Blood Sugar (MMOL/L): $bloodSugar +

+
+
+ Observation +
"""; + } +} diff --git a/lib/models/clients/careNoteFormsRequests/physical_intervention_form_html_request.dart b/lib/models/clients/careNoteFormsRequests/physical_intervention_form_html_request.dart new file mode 100644 index 0000000..ffc244a --- /dev/null +++ b/lib/models/clients/careNoteFormsRequests/physical_intervention_form_html_request.dart @@ -0,0 +1,150 @@ +import 'HtmlTableOption.dart'; + +class PhysicalInterventionFormHtmlRequest { + final String durationOfIncidents; + final String staffDebriefFormNumber; + final String nameOfWitnesses; + final String placeOfIncident; + final String priorToIntervention; + final String pbsFollowed; + final String reasonForPhysicalIntervention; + final String staffInvolvedInPI; + final String conditionOfSU; + final String howSuCalmed; + final List useOfForceNecessary; + final String pleaseExplain; + final String isParentContacted; + final String nameOfParentContacted; + final String parentContactedTime; + final String howParentContacted; + final String parentCarersComments; + final String howFormWasShared; + + PhysicalInterventionFormHtmlRequest({ + required this.durationOfIncidents, + required this.staffDebriefFormNumber, + required this.nameOfWitnesses, + required this.placeOfIncident, + required this.priorToIntervention, + required this.pbsFollowed, + required this.reasonForPhysicalIntervention, + required this.staffInvolvedInPI, + required this.conditionOfSU, + required this.howSuCalmed, + required this.useOfForceNecessary, + required this.pleaseExplain, + required this.isParentContacted, + required this.nameOfParentContacted, + required this.parentContactedTime, + required this.howParentContacted, + required this.parentCarersComments, + required this.howFormWasShared, + }); + + String toHtml() { + String ifParentContactedHtml = (isParentContacted == "Yes") + ? """
+

Name of Parent Contacted + $nameOfParentContacted

+
+
+

Contact time + $parentContactedTime

+
+
+

How parent was contacted + $howParentContacted

+
""" + : ""; + + return """
+

Duration of incident (Mins) + $durationOfIncidents

+
+
+

Staff Debrief Form Number + $staffDebriefFormNumber

+
+
+

Name of witnesses/adults present + $nameOfWitnesses

+
+
+

Place incident occurred + $placeOfIncident

+
+
+

What was used prior to intervention to defuse/deescalate the situation? + $priorToIntervention

+
+
+

Was the PBS followed and was it sufficient enough to manage this incident? + $pbsFollowed

+
+
+

Reason for physical intervention + $reasonForPhysicalIntervention

+
+
+

Staff involved in the physical intervention + $staffInvolvedInPI

+
+
+

Condition of service user following the incident, including breathing monitoring + $conditionOfSU

+
+
+

How was the service user calmed? + $howSuCalmed

+
+
+

Why was the use of force necessary?

+
+
+
+ + + + ${useOfForceNecessary.map((row) { + return """ + + + """; + }).join()} + +
${row.requirements} + ${row.isChecked() ? """ + Tick""" : """Close """} +
+
+
+
+

If ticked "Other" please explain + $pleaseExplain

+
+
+

Parent Contacted + $isParentContacted

+
+ +$ifParentContactedHtml +
+

Parent/carer’s comments + $parentCarersComments

+
+
+

How was this form shared with parents/carers? + $howFormWasShared

+
"""; + } +} diff --git a/lib/models/clients/careNoteFormsRequests/safeguarding_form_html_request.dart b/lib/models/clients/careNoteFormsRequests/safeguarding_form_html_request.dart new file mode 100644 index 0000000..0e82ae2 --- /dev/null +++ b/lib/models/clients/careNoteFormsRequests/safeguarding_form_html_request.dart @@ -0,0 +1,59 @@ +class SafeguardingFormHtmlRequest { + final String concernsAboutServiceUser; + final String voiceOfTheServiceUser; + final String areThereAnyImmediateRisks; + final String whatActionDoYouFeel; + final String comments; + final String yourName; + final String anyWitnesses; + final String dateTimeReporting; + final String yourNameDslDdsl; + final String actionTaken; + + SafeguardingFormHtmlRequest({ + required this.concernsAboutServiceUser, + required this.voiceOfTheServiceUser, + required this.areThereAnyImmediateRisks, + required this.whatActionDoYouFeel, + required this.comments, + required this.yourName, + required this.anyWitnesses, + required this.dateTimeReporting, + required this.yourNameDslDdsl, + required this.actionTaken, + }); + + String toHtml() { + return """
+

Concerns about the service user: $concernsAboutServiceUser

+
+
+

Voice of the service user: $voiceOfTheServiceUser

+
+
+

Are there any immediate risks: $areThereAnyImmediateRisks

+
+
+

What action do you feel should be taken?: $whatActionDoYouFeel

+
+
+

Comments: $comments

+
+
+

Your Name: $yourName

+
+
+

Any witnesses: $anyWitnesses

+
+
+

Date and time of reporting + $dateTimeReporting

+
+
+

To be completed by DSL/DDSL
Your Name: $yourNameDslDdsl

+
+
+


Action taken: $actionTaken

+
"""; + } +} diff --git a/lib/models/clients/careNoteFormsRequests/showering_and_bath_form_html_request.dart b/lib/models/clients/careNoteFormsRequests/showering_and_bath_form_html_request.dart new file mode 100644 index 0000000..c2b59e2 --- /dev/null +++ b/lib/models/clients/careNoteFormsRequests/showering_and_bath_form_html_request.dart @@ -0,0 +1,20 @@ +class ShoweringAndBathFormHtmlRequest { + final String showeringBathType; + final String comments; + + ShoweringAndBathFormHtmlRequest({ + required this.showeringBathType, + required this.comments, + }); + + String toHtml() { + return """ +
+

Type: $showeringBathType

+
+
+

Comments: ${comments.isNotEmpty ? comments : "No assistance required"}

+
+ """; + } +} diff --git a/lib/models/clients/careNoteFormsRequests/toileting_form_html_request.dart b/lib/models/clients/careNoteFormsRequests/toileting_form_html_request.dart new file mode 100644 index 0000000..d24a141 --- /dev/null +++ b/lib/models/clients/careNoteFormsRequests/toileting_form_html_request.dart @@ -0,0 +1,25 @@ +class ToiletingFormHtmlRequest { + final bool assistanceRequired; + final String assistanceType; + final String comments; + + ToiletingFormHtmlRequest({ + required this.assistanceRequired, + required this.assistanceType, + required this.comments, + }); + + String toHtml() { + return """ +
+

Was assistance required with toileting? ${assistanceRequired ? "Yes" : "No"}

+
+
+

Assistance type: ${assistanceType.isNotEmpty ? assistanceType : "Handled alone"}

+
+
+

Comments: ${comments.isNotEmpty ? comments : "No assistance required"}

+
+ """; + } +} diff --git a/lib/models/clients/care_note_category.dart b/lib/models/clients/care_note_category.dart new file mode 100644 index 0000000..32e75ca --- /dev/null +++ b/lib/models/clients/care_note_category.dart @@ -0,0 +1,62 @@ +class CareNoteCategory { + CareNoteCategory({ + this.iconPath, + this.category, + this.subcategories, + }); + + CareNoteCategory.fromJson(dynamic json) { + iconPath = json['iconPath']; + category = json['category']; + if (json['subcategories'] != null) { + subcategories = []; + json['subcategories'].forEach((v) { + subcategories?.add(Subcategories.fromJson(v)); + }); + } + } + + String? iconPath; + String? category; + List? subcategories; + + Map toJson() { + final map = {}; + map['iconPath'] = iconPath; + map['category'] = category; + if (subcategories != null) { + map['subcategories'] = subcategories?.map((v) => v.toJson()).toList(); + } + return map; + } +} + +class Subcategories { + Subcategories({ + this.iconPath, + this.name, + this.formType, + this.apiValue, + }); + + Subcategories.fromJson(dynamic json) { + iconPath = json['iconPath']; + name = json['name']; + formType = json['formType']; + apiValue = json['apiValue']; + } + + String? iconPath; + String? name; + String? formType; + String? apiValue; + + Map toJson() { + final map = {}; + map['iconPath'] = iconPath; + map['name'] = name; + map['formType'] = formType; + map['apiValue'] = apiValue; + return map; + } +} diff --git a/lib/models/clients/client_data_model.dart b/lib/models/clients/client_data_model.dart new file mode 100644 index 0000000..23d7023 --- /dev/null +++ b/lib/models/clients/client_data_model.dart @@ -0,0 +1,25 @@ +class ClientDataModel{ + String firstName = ""; + String lastName = ""; + String mobileNo = ""; + String email = ""; + String gender = ""; + DateTime dob = DateTime.now(); + String age = ""; + + ClientDataModel.empty(); + + ClientDataModel.addData( + {required this.firstName, + required this.lastName, + required this.mobileNo, + required this.email, + required this.gender, + required this.dob, + required this.age}); + + @override + String toString() { + return 'ClientDataModel{firstName: $firstName, lastName: $lastName, mobileNo: $mobileNo, email: $email, gender: $gender, dob: $dob, age: $age}'; + } +} \ No newline at end of file diff --git a/lib/models/clients/consent_details_model.dart b/lib/models/clients/consent_details_model.dart new file mode 100644 index 0000000..3f7e072 --- /dev/null +++ b/lib/models/clients/consent_details_model.dart @@ -0,0 +1,34 @@ +class ConsentDetailsModel{ + String id = ""; + String staffId = ""; + bool active = false; + String description = ""; + int v = 0; + DateTime createdAt =DateTime.now(); + DateTime updatedAt =DateTime.now(); + + ConsentDetailsModel.empty(); + ConsentDetailsModel.addData( + {required this.id, + required this.staffId, + required this.active, + required this.description, + required this.v, + required this.createdAt, + required this.updatedAt}); + + ConsentDetailsModel.fromJson(Map json){ + id = json['_id']??""; + staffId = json['staffId']??""; + active = json['active']??false; + description = json['description']??""; + v = json['__v']??-1; + createdAt = DateTime.tryParse(json['createdAt'])??DateTime.now(); + updatedAt = DateTime.tryParse(json['updatedAt'])??DateTime.now(); + } + + @override + String toString() { + return 'ConsentDetailsModel{id: $id, staffId: $staffId, active: $active, description: $description, v: $v, createdAt: $createdAt, updatedAt: $updatedAt}'; + } +} \ No newline at end of file diff --git a/lib/models/clients/documents_list_model.dart b/lib/models/clients/documents_list_model.dart new file mode 100644 index 0000000..e4ad95d --- /dev/null +++ b/lib/models/clients/documents_list_model.dart @@ -0,0 +1,338 @@ +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; + +class DocumentsListModel { + DocumentsListModel({ + required this.documentList, + required this.count, + required this.offset, + required this.limit, + }); + + List documentList = []; + int count = -1; + int offset = -1; + int limit = -1; + + DocumentsListModel.empty(); + + DocumentsListModel.fromJson(Map json) { + documentList = List.from(json['documentList'] ?? []) + .map((e) => DocumentModel.fromJson(e)) + .toList(); + count = json['count'] ?? -1; + offset = json['offset'] ?? -1; + limit = json['limit'] ?? -1; + } + + Map toJson() { + final data = {}; + data['documentList'] = documentList.map((e) => e.toJson()).toList(); + data['count'] = count; + data['offset'] = offset; + data['limit'] = limit; + return data; + } +} + +class DocumentModel { + DocumentModel({ + required this.id, + required this.docPath, + required this.details, + required this.title, + required this.userId, + required this.addedBy, + required this.createdAt, + required this.updatedAt, + }); + + String id = ""; + String docPath = ""; + String details = ""; + String title = ""; + UserData? userId; + UserData? addedBy; + String createdAt = ""; + String updatedAt = ""; + + DocumentModel.empty(); + + DocumentModel.fromJson(Map json) { + id = json['_id'] ?? ""; + docPath = json['docPath'] ?? ""; + details = json['details'] ?? ""; + title = json['title'] ?? ""; + userId = json['userId'] is Map ? UserData.fromJson(json['userId']) : null; + addedBy = + json['addedBy'] is Map ? UserData.fromJson(json['addedBy']) : null; + createdAt = json['createdAt'] ?? ""; + updatedAt = json['updatedAt'] ?? ""; + } + + Map toJson() { + final data = {}; + data['_id'] = id; + data['docPath'] = docPath; + data['details'] = details; + data['title'] = title; + data['userId'] = userId; + data['addedBy'] = addedBy; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + return data; + } +} + +// class UserId { +// UserId({ +// required this.fcmTokens, +// required this.location, +// required this.id, +// required this.userModelName, +// required this.name, +// required this.version, +// required this.email, +// required this.phoneNumber, +// required this.active, +// required this.role, +// required this.profilePictureUrl, +// required this.deviceId, +// required this.verificationCode, +// required this.isVerified, +// required this.approved, +// required this.blocked, +// required this.createdAt, +// required this.updatedAt, +// required this.V, +// required this.password, +// required this.userSettings, +// required this.modelId, +// }); +// +// Location location = Location.empty(); +// String id = ""; +// String userModelName = ""; +// String name = ""; +// String version = ""; +// String email = ""; +// String phoneNumber = ""; +// bool active = false; +// String role = ""; +// String profilePictureUrl = ""; +// String deviceId = ""; +// String verificationCode = ""; +// bool isVerified = false; +// bool approved = false; +// bool blocked = false; +// String createdAt = ""; +// String updatedAt = ""; +// int V = -1; +// String password = ""; +// String userSettings = ""; +// String modelId = ""; +// +// UserId.empty(); +// +// UserId.fromJson(Map json) { +// location = Location.fromJson(json['location'] ?? {}); +// id = json['_id'] ?? ""; +// userModelName = json['userModelName'] ?? ""; +// name = json['name'] ?? ""; +// version = json['version'] ?? ""; +// email = json['email'] ?? ""; +// phoneNumber = json['phoneNumber'] ?? ""; +// active = json['active'] ?? false; +// role = json['role'] ?? ""; +// profilePictureUrl = json['profile_picture_url'] ?? ""; +// deviceId = json['deviceId'] ?? ""; +// verificationCode = json['verification_code'] ?? ""; +// isVerified = json['is_verified'] ?? false; +// approved = json['approved'] ?? false; +// blocked = json['blocked'] ?? false; +// createdAt = json['createdAt'] ?? ""; +// updatedAt = json['updatedAt'] ?? ""; +// V = json['__v'] ?? -1; +// password = json['password'] ?? ""; +// userSettings = json['userSettings'] ?? ""; +// modelId = json['modelId'] ?? ""; +// } +// +// Map toJson() { +// final data = {}; +// data['fcm_tokens'] = fcmTokens.toJson(); +// data['location'] = location.toJson(); +// data['_id'] = id; +// data['userModelName'] = userModelName; +// data['name'] = name; +// data['version'] = version; +// data['email'] = email; +// data['phoneNumber'] = phoneNumber; +// data['active'] = active; +// data['role'] = role; +// data['profile_picture_url'] = profilePictureUrl; +// data['deviceId'] = deviceId; +// data['verification_code'] = verificationCode; +// data['is_verified'] = isVerified; +// data['approved'] = approved; +// data['blocked'] = blocked; +// data['createdAt'] = createdAt; +// data['updatedAt'] = updatedAt; +// data['__v'] = V; +// data['password'] = password; +// data['userSettings'] = userSettings; +// data['modelId'] = modelId; +// return data; +// } +// } +// +// class FcmTokens { +// FcmTokens({ +// required this.token, +// required this.deviceType, +// }); +// +// String token = ""; +// String deviceType = ""; +// +// FcmTokens.empty(); +// +// FcmTokens.fromJson(Map json) { +// token = json['token'] ?? ""; +// deviceType = json['deviceType'] ?? ""; +// } +// +// Map toJson() { +// final data = {}; +// data['token'] = token; +// data['deviceType'] = deviceType; +// return data; +// } +// } +// +// class Location { +// Location({ +// required this.type, +// required this.coordinates, +// }); +// +// String type = ""; +// List coordinates = []; +// +// Location.empty(); +// +// Location.fromJson(Map json) { +// type = json['type'] ?? ""; +// coordinates = List.castFrom(json['coordinates'] ?? []); +// } +// +// Map toJson() { +// final data = {}; +// data['type'] = type; +// data['coordinates'] = coordinates; +// return data; +// } +// } +// +// class AddedBy { +// AddedBy({ +// required this.fcmTokens, +// required this.location, +// required this.id, +// required this.userModelName, +// required this.name, +// required this.version, +// required this.email, +// required this.phoneNumber, +// required this.active, +// required this.role, +// required this.profilePictureUrl, +// required this.deviceId, +// required this.verificationCode, +// required this.isVerified, +// required this.approved, +// required this.blocked, +// required this.createdAt, +// required this.updatedAt, +// required this.V, +// required this.password, +// required this.userSettings, +// required this.modelId, +// }); +// +// FcmTokens fcmTokens = FcmTokens.empty(); +// Location location = Location.empty(); +// String id = ""; +// String userModelName = ""; +// String name = ""; +// String version = ""; +// String email = ""; +// String phoneNumber = ""; +// bool active = 0 == 1; +// String role = ""; +// String profilePictureUrl = ""; +// String deviceId = ""; +// String verificationCode = ""; +// bool isVerified = 1 == 0; +// bool approved = 1 == 0; +// bool blocked = 1 == 0; +// String createdAt = ""; +// String updatedAt = ""; +// int V = 1; +// String password = ""; +// String userSettings = ""; +// String modelId = ""; +// +// AddedBy.empty(); +// +// AddedBy.fromJson(Map json) { +// fcmTokens = FcmTokens.fromJson(json['fcm_tokens'] ?? FcmTokens.empty()); +// location = Location.fromJson(json['location'] ?? Location.empty()); +// id = json['_id'] ?? ""; +// userModelName = json['userModelName'] ?? ""; +// name = json['name'] ?? ""; +// version = json['version'] ?? ""; +// email = json['email'] ?? ""; +// phoneNumber = json['phoneNumber'] ?? ""; +// active = json['active'] ?? false; +// role = json['role'] ?? ""; +// profilePictureUrl = json['profile_picture_url'] ?? ""; +// deviceId = json['deviceId'] ?? ""; +// verificationCode = json['verification_code'] ?? ""; +// isVerified = json['is_verified'] ?? false; +// approved = json['approved'] ?? false; +// blocked = json['blocked'] ?? false; +// createdAt = json['createdAt'] ?? ""; +// updatedAt = json['updatedAt'] ?? ""; +// V = json['__v'] ?? -1; +// password = json['password'] ?? ""; +// userSettings = json['userSettings'] ?? ""; +// modelId = json['modelId'] ?? ""; +// } +// +// Map toJson() { +// final data = {}; +// data['fcm_tokens'] = fcmTokens.toJson(); +// data['location'] = location.toJson(); +// data['_id'] = id; +// data['userModelName'] = userModelName; +// data['name'] = name; +// data['version'] = version; +// data['email'] = email; +// data['phoneNumber'] = phoneNumber; +// data['active'] = active; +// data['role'] = role; +// data['profile_picture_url'] = profilePictureUrl; +// data['deviceId'] = deviceId; +// data['verification_code'] = verificationCode; +// data['is_verified'] = isVerified; +// data['approved'] = approved; +// data['blocked'] = blocked; +// data['createdAt'] = createdAt; +// data['updatedAt'] = updatedAt; +// data['__v'] = V; +// data['password'] = password; +// data['userSettings'] = userSettings; +// data['modelId'] = modelId; +// return data; +// } +// } diff --git a/lib/models/clients/memoryListResponse/MemoryListData.dart b/lib/models/clients/memoryListResponse/MemoryListData.dart new file mode 100644 index 0000000..730a355 --- /dev/null +++ b/lib/models/clients/memoryListResponse/MemoryListData.dart @@ -0,0 +1,52 @@ +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; + +class MemoryListData { + MemoryListData({ + this.addedBy, + this.userId, + this.note, + this.filePath, + this.isDeleted, + this.createdAt, + this.updatedAt, + this.id, + }); + + MemoryListData.fromJson(dynamic json) { + id = json['_id']; + addedBy = + json['addedBy'] != null ? UserData.fromJson(json['addedBy']) : null; + userId = json['userId'] != null ? UserData.fromJson(json['userId']) : null; + note = json['note']; + filePath = json['filePath']; + isDeleted = json['isDeleted']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + } + + String? id; + UserData? addedBy; + UserData? userId; + String? note; + String? filePath; + bool? isDeleted; + String? createdAt; + String? updatedAt; + + Map toJson() { + final map = {}; + map['_id'] = id; + if (addedBy != null) { + map['addedBy'] = addedBy?.toJson(); + } + if (userId != null) { + map['userId'] = userId?.toJson(); + } + map['note'] = note; + map['filePath'] = filePath; + map['isDeleted'] = isDeleted; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + return map; + } +} diff --git a/lib/models/clients/memoryListResponse/MemoryListResponse.dart b/lib/models/clients/memoryListResponse/MemoryListResponse.dart new file mode 100644 index 0000000..5c24b68 --- /dev/null +++ b/lib/models/clients/memoryListResponse/MemoryListResponse.dart @@ -0,0 +1,30 @@ +import 'MemoryListResponseData.dart'; + +class MemoryListResponse { + MemoryListResponse({ + this.success, + this.status, + this.message, + this.data,}); + + MemoryListResponse.fromJson(dynamic json) { + status = json['status']; + message = json['message']; + data = json['data'] != null ? MemoryListResponseData.fromJson(json['data']) : null; + } + bool? success; + String? status; + String? message; + MemoryListResponseData? data; + + Map toJson() { + final map = {}; + map['status'] = status; + map['message'] = message; + if (data != null) { + map['data'] = data?.toJson(); + } + return map; + } + +} \ No newline at end of file diff --git a/lib/models/clients/memoryListResponse/MemoryListResponseData.dart b/lib/models/clients/memoryListResponse/MemoryListResponseData.dart new file mode 100644 index 0000000..c0391ea --- /dev/null +++ b/lib/models/clients/memoryListResponse/MemoryListResponseData.dart @@ -0,0 +1,38 @@ +import 'MemoryListData.dart'; + +class MemoryListResponseData { + MemoryListResponseData({ + this.list, + this.count, + this.offset, + this.limit, + }); + + MemoryListResponseData.fromJson(dynamic json) { + if (json['list'] != null) { + list = []; + json['list'].forEach((v) { + list?.add(MemoryListData.fromJson(v)); + }); + } + count = json['count']; + offset = json['offset']; + limit = json['limit']; + } + + List? list; + int? count; + int? offset; + int? limit; + + Map toJson() { + final map = {}; + if (list != null) { + map['list'] = list?.map((v) => v.toJson()).toList(); + } + map['count'] = count; + map['offset'] = offset; + map['limit'] = limit; + return map; + } +} diff --git a/lib/models/clients/recent_incidents_model.dart b/lib/models/clients/recent_incidents_model.dart new file mode 100644 index 0000000..967bd26 --- /dev/null +++ b/lib/models/clients/recent_incidents_model.dart @@ -0,0 +1,48 @@ +import 'package:quill_html_editor/quill_html_editor.dart'; + +class RecentIncidentsModel{ + String incidentTitle = ""; + String incidentId = ""; + String userId = ""; + String note = ""; + int incidentDate = 0; + bool active = false; + String createdAt = ""; + String updatedAt = ""; + int v = 0; + QuillEditorController quillController = QuillEditorController(); + + RecentIncidentsModel.empty(); + + RecentIncidentsModel.fromJson(Map json){ + incidentId = json['_id']??""; + userId = json['userId']??""; + note = json['note']??""; + incidentDate = json['incidentDate']??0; + active = json['active']??false; + createdAt = json['createdAt']??""; + updatedAt = json['updatedAt']??""; + incidentTitle = json['incidentTitle']??""; + v = json['__v']??0; + } + + Map toJson(){ + Map json = { + '_id' : incidentId, + 'userId' : userId, + 'note' : note, + 'incidentDate' : incidentDate, + 'active' : active, + 'createdAt' : createdAt, + 'updatedAt' : updatedAt, + 'incidentTitle' : incidentTitle, + '__v' : v, + }; + return json; + } + + @override + String toString() { + return 'RecentIncidentsModel{incidentId: $incidentId, userId: $userId, note: $note, incidentDate: $incidentDate, active: $active, createdAt: $createdAt, updatedAt: $updatedAt, v: $v}'; + } +} \ No newline at end of file diff --git a/lib/models/clients/riskAssessmentResponse/GetRiskAssessmentResponse.dart b/lib/models/clients/riskAssessmentResponse/GetRiskAssessmentResponse.dart new file mode 100644 index 0000000..7edf746 --- /dev/null +++ b/lib/models/clients/riskAssessmentResponse/GetRiskAssessmentResponse.dart @@ -0,0 +1,35 @@ +import 'RiskAssessmentData.dart'; + +class GetRiskAssessmentResponse { + GetRiskAssessmentResponse({ + this.success, + this.status, + this.message, + this.data,}); + + GetRiskAssessmentResponse.fromJson(dynamic json) { + status = json['status']; + message = json['message']; + if (json['data'] != null) { + data = []; + json['data'].forEach((v) { + data?.add(RiskAssessmentData.fromJson(v)); + }); + } + } + bool? success; + String? status; + String? message; + List? data; + + Map toJson() { + final map = {}; + map['status'] = status; + map['message'] = message; + if (data != null) { + map['data'] = data?.map((v) => v.toJson()).toList(); + } + return map; + } + +} \ No newline at end of file diff --git a/lib/models/clients/riskAssessmentResponse/InPlace.dart b/lib/models/clients/riskAssessmentResponse/InPlace.dart new file mode 100644 index 0000000..d5f3048 --- /dev/null +++ b/lib/models/clients/riskAssessmentResponse/InPlace.dart @@ -0,0 +1,20 @@ +class InPlace { + InPlace({ + this.y, + this.n,}); + + InPlace.fromJson(dynamic json) { + y = json['y']; + n = json['n']; + } + int? y; + int? n; + + Map toJson() { + final map = {}; + map['y'] = y; + map['n'] = n; + return map; + } + +} \ No newline at end of file diff --git a/lib/models/clients/riskAssessmentResponse/PureRiskRating.dart b/lib/models/clients/riskAssessmentResponse/PureRiskRating.dart new file mode 100644 index 0000000..268e237 --- /dev/null +++ b/lib/models/clients/riskAssessmentResponse/PureRiskRating.dart @@ -0,0 +1,24 @@ +class PureRiskRating { + PureRiskRating({ + this.c, + this.l, + this.r,}); + + PureRiskRating.fromJson(dynamic json) { + c = json['c']; + l = json['l']; + r = json['r']; + } + int? c; + int? l; + int? r; + + Map toJson() { + final map = {}; + map['c'] = c; + map['l'] = l; + map['r'] = r; + return map; + } + +} \ No newline at end of file diff --git a/lib/models/clients/riskAssessmentResponse/ResidualRiskRating.dart b/lib/models/clients/riskAssessmentResponse/ResidualRiskRating.dart new file mode 100644 index 0000000..de26291 --- /dev/null +++ b/lib/models/clients/riskAssessmentResponse/ResidualRiskRating.dart @@ -0,0 +1,24 @@ +class ResidualRiskRating { + ResidualRiskRating({ + this.c, + this.l, + this.r,}); + + ResidualRiskRating.fromJson(dynamic json) { + c = json['c']; + l = json['l']; + r = json['r']; + } + int? c; + int? l; + int? r; + + Map toJson() { + final map = {}; + map['c'] = c; + map['l'] = l; + map['r'] = r; + return map; + } + +} \ No newline at end of file diff --git a/lib/models/clients/riskAssessmentResponse/RiskAssessmentData.dart b/lib/models/clients/riskAssessmentResponse/RiskAssessmentData.dart new file mode 100644 index 0000000..a39cd76 --- /dev/null +++ b/lib/models/clients/riskAssessmentResponse/RiskAssessmentData.dart @@ -0,0 +1,72 @@ +import 'PureRiskRating.dart'; +import 'InPlace.dart'; +import 'ResidualRiskRating.dart'; + +class RiskAssessmentData { + RiskAssessmentData({ + this.pureRiskRating, + this.inPlace, + this.residualRiskRating, + this.id, + this.hazard, + this.personsExposedToHazard, + this.riskIdentified, + this.coldMeasureRequired, + this.userId, + this.createdAt, + this.updatedAt, + }); + + RiskAssessmentData.fromJson(dynamic json) { + pureRiskRating = json['pureRiskRating'] != null + ? PureRiskRating.fromJson(json['pureRiskRating']) + : null; + inPlace = + json['inPlace'] != null ? InPlace.fromJson(json['inPlace']) : null; + residualRiskRating = json['residualRiskRating'] != null + ? ResidualRiskRating.fromJson(json['residualRiskRating']) + : null; + id = json['_id']; + hazard = json['hazard']; + personsExposedToHazard = json['personsExposedToHazard']; + riskIdentified = json['riskIdentified']; + coldMeasureRequired = json['coldMeasureRequired']; + userId = json['userId']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + } + + PureRiskRating? pureRiskRating; + InPlace? inPlace; + ResidualRiskRating? residualRiskRating; + String? id; + String? hazard; + String? personsExposedToHazard; + String? riskIdentified; + String? coldMeasureRequired; + String? userId; + String? createdAt; + String? updatedAt; + + Map toJson() { + final map = {}; + if (pureRiskRating != null) { + map['pureRiskRating'] = pureRiskRating?.toJson(); + } + if (inPlace != null) { + map['inPlace'] = inPlace?.toJson(); + } + if (residualRiskRating != null) { + map['residualRiskRating'] = residualRiskRating?.toJson(); + } + map['_id'] = id; + map['hazard'] = hazard; + map['personsExposedToHazard'] = personsExposedToHazard; + map['riskIdentified'] = riskIdentified; + map['coldMeasureRequired'] = coldMeasureRequired; + map['userId'] = userId; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + return map; + } +} diff --git a/lib/models/clients/service_users_model.dart b/lib/models/clients/service_users_model.dart new file mode 100644 index 0000000..6c60a56 --- /dev/null +++ b/lib/models/clients/service_users_model.dart @@ -0,0 +1,350 @@ +class ServiceUserModel { + AboutPatient aboutPatient = AboutPatient.empty(); + String id = ""; + String suLastName = ""; + String suFirstMiddleName = ""; + String suPreferredName = ""; + String name = ""; + String suSsn = ""; + String providerName = ""; + String suSex = ""; + String suTitle = ""; + DateTime suDob = DateTime.now(); + String suAge = ""; + String suReferredBy = ""; + String suFamilyHead = ""; + String suAddress1 = ""; + String suAddress2 = ""; + String suCity = ""; + String suState = ""; + String suZip = ""; + DateTime suFirstVisitDate = DateTime.now(); + DateTime suLastVisitDate = DateTime.now(); + List suProvider = []; + bool currSu = false; + String suHomePhone = ""; + String suWorkPhone = ""; + String suMobileHomeNo = ""; + String suMobileWorkNo = ""; + String suEmailHome = ""; + String suEmailWork = ""; + String suPrefHomeNo = ""; + String suPrefWorkNo = ""; + String suEmergencyContact = ""; + String seMedicalAlert = ""; + String suNote = ""; + List diagnosises = []; + String user = ""; + List shifts = []; + List serviceUserMedications = []; + List homeVisitSignOut = []; + List srUsShiftsRequired = []; + List suEnquiries = []; + bool active = false; + DateTime createdAt = DateTime.now(); + DateTime updatedAt = DateTime.now(); + int v = 0; + + ServiceUserModel({ + required this.aboutPatient, + required this.id, + required this.suLastName, + required this.suFirstMiddleName, + required this.suPreferredName, + required this.name, + required this.suSsn, + required this.providerName, + required this.suSex, + required this.suTitle, + required this.suDob, + required this.suAge, + required this.suReferredBy, + required this.suFamilyHead, + required this.suAddress1, + required this.suAddress2, + required this.suCity, + required this.suState, + required this.suZip, + required this.suFirstVisitDate, + required this.suLastVisitDate, + required this.suProvider, + required this.currSu, + required this.suHomePhone, + required this.suWorkPhone, + required this.suMobileHomeNo, + required this.suMobileWorkNo, + required this.suEmailHome, + required this.suEmailWork, + required this.suPrefHomeNo, + required this.suPrefWorkNo, + required this.suEmergencyContact, + required this.seMedicalAlert, + required this.suNote, + required this.diagnosises, + required this.user, + required this.shifts, + required this.serviceUserMedications, + required this.homeVisitSignOut, + required this.srUsShiftsRequired, + required this.suEnquiries, + required this.active, + required this.createdAt, + required this.updatedAt, + required this.v, + }); + + String get phoneNo { + if (suMobileWorkNo.isNotEmpty) { + return suMobileWorkNo; + } else if (suMobileHomeNo.isNotEmpty) { + return suMobileHomeNo; + } else if (suEmergencyContact.isNotEmpty) { + return suEmergencyContact; + } else if (suPrefHomeNo.isNotEmpty) { + return suPrefHomeNo; + } else if (suPrefWorkNo.isNotEmpty) { + return suPrefWorkNo; + } else if (suWorkPhone.isNotEmpty) { + return suWorkPhone; + } else if (suHomePhone.isNotEmpty) { + return suHomePhone; + } + return "000000000000"; + } + + String get nameOfUser { + if (suFirstMiddleName.isNotEmpty) { + return suFirstMiddleName; + } else if (suLastName.isNotEmpty) { + return suLastName; + } else if (suPreferredName.isNotEmpty) { + return suPreferredName; + } else if (name.isNotEmpty) { + return name; + } + return "Name"; + } + + String get homeAddress { + if (suAddress1.isNotEmpty) { + return suAddress1; + } else if (suAddress2.isNotEmpty) { + return suAddress2; + } + return "Address"; + } + + ServiceUserModel.empty(); + + ServiceUserModel.fromJson(Map json) { + aboutPatient = json['aboutPatient'] != null + ? AboutPatient.fromJson(json['aboutPatient']) + : AboutPatient.empty(); + id = json['_id'] ?? ""; + suLastName = json['suLastName'] ?? ""; + suFirstMiddleName = json['suFirstMiddleName'] ?? ""; + suPreferredName = json['suPreferredName'] ?? ""; + name = json['name'] ?? ""; + suSsn = json['suSsn'] ?? ""; + providerName = json['providerName'] ?? ""; + suSex = json['suSex'] ?? ""; + suTitle = json['suTitle'] ?? ""; + suDob = json['suDOB'] != null + ? DateTime.parse(json['suDOB']).toLocal() + : DateTime.now(); + suAge = json['suAge'] ?? ''; + suReferredBy = json['suReferredBY'] ?? ""; + suFamilyHead = json['suFamilyHead'] ?? ""; + suAddress1 = json['suAddress1'] ?? ""; + suAddress2 = json['suAddress2'] ?? ""; + suCity = json['suCity'] ?? ""; + suState = json['suState'] ?? ""; + suZip = json['suZip'] ?? ""; + suFirstVisitDate = json['suFirstVisitDate'] != null + ? DateTime.parse(json['suFirstVisitDate']).toLocal() + : DateTime.now(); + suLastVisitDate = json['suLastVisitDate'] != null + ? DateTime.parse(json['suLastVisitDate']).toLocal() + : DateTime.now(); + if (json['suProvider'] != null) { + suProvider = []; + json['suProvider'].forEach((v) { + suProvider.add(v); + }); + } + currSu = json['currSU'] ?? false; + suHomePhone = json['suHomePhone'] ?? ''; + suWorkPhone = json['suWorkPhone'] ?? ''; + suMobileHomeNo = json['suMobileHomeNo'] ?? ''; + suMobileWorkNo = json['suMobileWorkNo'] ?? ''; + suEmailHome = json['suEmailHome'] ?? ''; + suEmailWork = json['suEmailWork'] ?? ''; + suPrefHomeNo = json['suPrefHomeNo'] ?? ''; + suPrefWorkNo = json['suPrefWorkNo'] ?? ''; + suEmergencyContact = json['suEmergencyContact'] ?? ''; + seMedicalAlert = json['seMedicalAlert'] ?? ''; + suNote = json['suNote'] ?? ''; + if (json['diagnosises'] != null) { + diagnosises = []; + json['diagnosises'].forEach((v) { + diagnosises.add(Diagnosise.fromJson(v)); + }); + } + user = json['user'] ?? ""; + if (json['shifts'] != null) { + shifts = []; + json['shifts'].forEach((v) { + shifts.add(v); + }); + } + if (json['serviceUserMedications'] != null) { + serviceUserMedications = []; + json['serviceUserMedications'].forEach((v) { + serviceUserMedications.add(v); + }); + } + if (json['homeVisitSignOut'] != null) { + homeVisitSignOut = []; + json['homeVisitSignOut'].forEach((v) { + homeVisitSignOut.add(v); + }); + } + if (json['srUsShiftsRequired'] != null) { + srUsShiftsRequired = []; + json['srUsShiftsRequired'].forEach((v) { + srUsShiftsRequired.add(v); + }); + } + if (json['suEnquiries'] != null) { + suEnquiries = []; + json['suEnquiries'].forEach((v) { + suEnquiries.add(v); + }); + } + active = json['active'] ?? false; + createdAt = json['createdAt'] != null + ? DateTime.parse(json['createdAt']).toLocal() + : DateTime.now(); + updatedAt = json['updatedAt'] != null + ? DateTime.parse(json['updatedAt']).toLocal() + : DateTime.now(); + v = json['__v'] ?? 0; + } + + Map toJson() { + final Map data = {}; + // data['aboutPatient'] = this.aboutPatient.toJson(); + data['_id'] = id; + data['suLastName'] = suLastName; + data['suFirstMiddleName'] = suFirstMiddleName; + data['suPreferredName'] = suPreferredName; + data['name'] = name; + data['suSsn'] = suSsn; + data['providerName'] = providerName; + data['suSex'] = suSex; + data['suTitle'] = suTitle; + data['suDOB'] = suDob; + data['suAge'] = suAge; + data['suReferredBY'] = suReferredBy; + data['suFamilyHead'] = suFamilyHead; + data['suAddress1'] = suAddress1; + data['suAddress2'] = suAddress2; + data['suCity'] = suCity; + data['suState'] = suState; + data['suZip'] = suZip; + data['suFirstVisitDate'] = suFirstVisitDate; + data['suLastVisitDate'] = suLastVisitDate; + data['suProvider'] = suProvider.map((v) => v.toJson()).toList(); + data['currSU'] = currSu; + data['suHomePhone'] = suHomePhone; + data['suWorkPhone'] = suWorkPhone; + data['suMobileHomeNo'] = suMobileHomeNo; + data['suMobileWorkNo'] = suMobileWorkNo; + data['suEmailHome'] = suEmailHome; + data['suEmailWork'] = suEmailWork; + data['suPrefHomeNo'] = suPrefHomeNo; + data['suPrefWorkNo'] = suPrefWorkNo; + data['suEmergencyContact'] = suEmergencyContact; + data['seMedicalAlert'] = seMedicalAlert; + data['suNote'] = suNote; + // data['diagnosises'] = this.diagnosises.map((v) => v.toJson()).toList(); + data['user'] = user; + data['shifts'] = shifts.map((v) => v.toJson()).toList(); + data['serviceUserMedications'] = + serviceUserMedications.map((v) => v.toJson()).toList(); + data['homeVisitSignOut'] = homeVisitSignOut.map((v) => v.toJson()).toList(); + data['srUsShiftsRequired'] = + srUsShiftsRequired.map((v) => v.toJson()).toList(); + data['suEnquiries'] = suEnquiries.map((v) => v.toJson()).toList(); + data['active'] = active; + data['createdAt'] = createdAt; + data['updatedAt'] = updatedAt; + data['__v'] = v; + return data; + } + + @override + String toString() { + return 'ServiceUserModel{aboutPatient: $aboutPatient, id: $id, suLastName: $suLastName, suFirstMiddleName: $suFirstMiddleName, suPreferredName: $suPreferredName, name: $name, suSsn: $suSsn, providerName: $providerName, suSex: $suSex, suTitle: $suTitle, suDob: $suDob, suAge: $suAge, suReferredBy: $suReferredBy, suFamilyHead: $suFamilyHead, suAddress1: $suAddress1, suAddress2: $suAddress2, suCity: $suCity, suState: $suState, suZip: $suZip, suFirstVisitDate: $suFirstVisitDate, suLastVisitDate: $suLastVisitDate, suProvider: $suProvider, currSu: $currSu, suHomePhone: $suHomePhone, suWorkPhone: $suWorkPhone, suMobileHomeNo: $suMobileHomeNo, suMobileWorkNo: $suMobileWorkNo, suEmailHome: $suEmailHome, suEmailWork: $suEmailWork, suPrefHomeNo: $suPrefHomeNo, suPrefWorkNo: $suPrefWorkNo, suEmergencyContact: $suEmergencyContact, seMedicalAlert: $seMedicalAlert, suNote: $suNote, diagnosises: $diagnosises, user: $user, shifts: $shifts, serviceUserMedications: $serviceUserMedications, homeVisitSignOut: $homeVisitSignOut, srUsShiftsRequired: $srUsShiftsRequired, suEnquiries: $suEnquiries, active: $active, createdAt: $createdAt, updatedAt: $updatedAt, v: $v}'; + } +} + +class AboutPatient { + String aboutText = ""; + DateTime aboutDate = DateTime.now(); + String aboutBy = ""; + + AboutPatient({ + required this.aboutText, + required this.aboutDate, + required this.aboutBy, + }); + + AboutPatient.empty(); + + AboutPatient.fromJson(Map json) { + aboutText = json['aboutText'] ?? ""; + aboutDate = json['aboutDate'] != null + ? DateTime.parse(json['aboutDate']).toLocal() + : DateTime.now(); + aboutBy = json['aboutBy'] ?? ""; + } + + @override + String toString() { + return 'AboutPatient{aboutText: $aboutText, aboutDate: $aboutDate, aboutBy: $aboutBy}'; + } +} + +class Diagnosise { + String diagnosisText = ""; + DateTime diagnosisDate = DateTime.now(); + String diagnosisBy = ""; + bool isCurrentDiagnosis = false; + String id = ""; + + Diagnosise({ + required this.diagnosisText, + required this.diagnosisDate, + required this.diagnosisBy, + required this.isCurrentDiagnosis, + required this.id, + }); + + Diagnosise.empty(); + + Diagnosise.fromJson(Map json) { + diagnosisText = json['diagnosisText'] ?? ""; + diagnosisDate = json['diagnosisDate'] != null + ? DateTime.parse(json['diagnosisDate']).toLocal() + : DateTime.now(); + diagnosisBy = json['diagnosisBy'] ?? ""; + isCurrentDiagnosis = json['isCurrentDiagnosis'] ?? false; + id = json['_id'] ?? ""; + } + + @override + String toString() { + return 'Diagnosise{diagnosisText: $diagnosisText, diagnosisDate: $diagnosisDate, diagnosisBy: $diagnosisBy, isCurrentDiagnosis: $isCurrentDiagnosis, id: $id}'; + } +} diff --git a/lib/models/create_care_plan_request.dart b/lib/models/create_care_plan_request.dart new file mode 100644 index 0000000..c02626e --- /dev/null +++ b/lib/models/create_care_plan_request.dart @@ -0,0 +1,105 @@ +class CreateCarePlanRequest { + CreateCarePlanRequest({ + this.eventDateTime, + this.userId, + this.addedby, + this.noteDetails, + this.noteType, + this.title, + this.flag, + this.isHTML, + this.healthNote, + this.category, + this.complaint, + this.moodRating, + }); + + int? eventDateTime; + String? userId; + String? addedby; + String? noteDetails; + String? noteType; + String? title; + bool? flag; + bool? isHTML; + String? healthNote; + String? category; + String? complaint; + String? moodRating; + + //------------------------------------- + Map toJson() { + final map = {}; + if (eventDateTime != null) map['eventDateTime'] = eventDateTime; + if (userId != null) map['userId'] = userId; + if (addedby != null) map['addedby'] = addedby; + if (noteDetails != null) map['noteDetails'] = noteDetails; + if (noteType != null) map['noteType'] = noteType; + if (title != null) map['title'] = title; + map['flag'] = flag ?? false; + map['isHTML'] = isHTML ?? false; + if (healthNote != null) map['healthNote'] = healthNote; + if (category != null) map['category'] = category; + if (complaint != null) map['complaint'] = complaint; + if (moodRating != null) map['moodRating'] = moodRating; + return map; + } + + static String heightWeightHtmlReq( + String height, String weight, String comments) { + return """
+

Height - (CM): $height

+
+
+

Weight - (KG): $weight

+
+
+

Comments: $comments

+
"""; + } + + static String injuryHealthIssueHtmlReq({ + required String nameOfWitnesses, + required String placeOfAccident, + required String accidentDescription, + required String recordOfInjury, + required String conditionOfPatient, + required String isParentContacted, + String? howParentContacted, + String? nameOfParentContacted, + String? parentContactedTime, + }) { + String ifParentContactedHtml = (isParentContacted == "Yes") + ? """
+

If Yes how parent was contacted?: $howParentContacted

+
+
+

Name of Parent Contacted: + $nameOfParentContacted
+ Contacted at: + $parentContactedTime +

+
""" + : ""; + + return """
+

Name of witnesses/adults present: $nameOfWitnesses

+
+
+

Place accident occured: $placeOfAccident

+
+
+

Description how the accident occured: $accidentDescription

+
+
+

Record of any injury and action taken: $recordOfInjury

+
+
+

Condition of the patient following of the accident: $conditionOfPatient

+
+
+

Parent Contacted: $isParentContacted

+
$ifParentContactedHtml + """; + } +} diff --git a/lib/models/export_models.dart b/lib/models/export_models.dart new file mode 100644 index 0000000..22fb0a1 --- /dev/null +++ b/lib/models/export_models.dart @@ -0,0 +1,6 @@ +export 'response_model.dart'; +export 'mark_dates_model.dart'; +export 'rota_shift_model.dart'; +export 'holiday_model.dart'; +export 'user_model.dart'; +export 'messages_list_model.dart'; \ No newline at end of file diff --git a/lib/models/holiday_model.dart b/lib/models/holiday_model.dart new file mode 100644 index 0000000..e2ad05a --- /dev/null +++ b/lib/models/holiday_model.dart @@ -0,0 +1,21 @@ +class HolidayModel{ + + String carriedOver = ""; + String holidayEntitlement = ""; + String holidayAllowance = ""; + String remainingHolidays = ""; + String timeLeftBeforeYearEnd = ""; + + HolidayModel({ + required this.carriedOver, + required this.holidayEntitlement, + required this.holidayAllowance, + required this.remainingHolidays, + required this.timeLeftBeforeYearEnd, + }); + + @override + String toString() { + return 'HolidayModel{carriedOver: $carriedOver, holidayEntitlement: $holidayEntitlement, holidayAllowance: $holidayAllowance, remainingHolidays: $remainingHolidays, timeLeftBeforeYearEnd: $timeLeftBeforeYearEnd}'; + } +} \ No newline at end of file diff --git a/lib/models/mark_dates_model.dart b/lib/models/mark_dates_model.dart new file mode 100644 index 0000000..b25af86 --- /dev/null +++ b/lib/models/mark_dates_model.dart @@ -0,0 +1,13 @@ +class MarkDatesModel{ + DateTime date = DateTime.now(); + String title = "title"; + + MarkDatesModel.empty(); + + MarkDatesModel.addDate({required this.date,required this.title}); + + @override + String toString() { + return 'MarkDatesModel{date: $date, title: $title}'; + } +} \ No newline at end of file diff --git a/lib/models/messages_list_model.dart b/lib/models/messages_list_model.dart new file mode 100644 index 0000000..2b8f389 --- /dev/null +++ b/lib/models/messages_list_model.dart @@ -0,0 +1,43 @@ +import 'package:ftc_mobile_app/view/screens/chat/arguments/group_data_args.dart'; + +class MessagesListModel { + String otherUserId = ""; + String image = ""; + String title = ""; + String previewOfLastMessage = ""; + String messageType = ""; + + //in milliseconds + int date = DateTime.now().millisecondsSinceEpoch; + int noOfMessages = 0; + bool isRecent = false; + bool isGroup = false; + GroupDataArgs? groupData; + + MessagesListModel.empty() { + date = DateTime.now().millisecondsSinceEpoch; + } + + MessagesListModel({ + this.otherUserId = "", + this.image = "", + this.title = "", + this.previewOfLastMessage = "", + this.messageType = "", + required this.date, + // this.messageDateTime = '', + this.noOfMessages = 0, + // this.personalMessageIndex = -1, + // this.groupMessageIndex = -1, + this.isRecent = false, + this.isGroup = false, + this.groupData, + }); + + @override + String toString() { + return 'MessagesListModel{profilePic: $image, nameOfSender: $title, previewOfLastMessage: $previewOfLastMessage, date: $date, ' + // 'messageDayTime: $messageDateTime, ' + 'noOfMessages: $noOfMessages, isRecent: $isRecent, isGroup: $isGroup}'; + } +} diff --git a/lib/models/mood_rating_data.dart b/lib/models/mood_rating_data.dart new file mode 100644 index 0000000..65313ab --- /dev/null +++ b/lib/models/mood_rating_data.dart @@ -0,0 +1,6 @@ +class MoodRatingData { + final String icon; + final String name; + + MoodRatingData({required this.icon, required this.name}); +} diff --git a/lib/models/profileData/FcmTokens.dart b/lib/models/profileData/FcmTokens.dart new file mode 100644 index 0000000..31c6c81 --- /dev/null +++ b/lib/models/profileData/FcmTokens.dart @@ -0,0 +1,23 @@ +class FcmTokens { + FcmTokens({ + this.token, + this.deviceType, + }); + + FcmTokens.empty(); + + FcmTokens.fromJson(dynamic json) { + token = json['token']; + deviceType = json['deviceType']; + } + + String? token; + String? deviceType; + + Map toJson() { + final map = {}; + map['token'] = token; + map['deviceType'] = deviceType; + return map; + } +} diff --git a/lib/models/profileData/LocationData.dart b/lib/models/profileData/LocationData.dart new file mode 100644 index 0000000..e3c93fd --- /dev/null +++ b/lib/models/profileData/LocationData.dart @@ -0,0 +1,22 @@ +class LocationData { + LocationData({ + this.type, + this.coordinates,}); + + LocationData.empty(); + + LocationData.fromJson(dynamic json) { + type = json['type']; + coordinates = json['coordinates'] != null ? json['coordinates'].cast() : []; + } + String? type; + List? coordinates; + + Map toJson() { + final map = {}; + map['type'] = type; + map['coordinates'] = coordinates; + return map; + } + +} \ No newline at end of file diff --git a/lib/models/profileData/ProfileModelData.dart b/lib/models/profileData/ProfileModelData.dart new file mode 100644 index 0000000..eb448db --- /dev/null +++ b/lib/models/profileData/ProfileModelData.dart @@ -0,0 +1,792 @@ +// import 'package:ftc_mobile_app/models/profileData/LocationData.dart'; +// import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +// import 'FcmTokens.dart'; +// +// class ProfileDataModel { +// ProfileDataModel({ +// this.statusCode, +// this.statusDescription, +// this.data, +// }); +// +// ProfileDataModel.fromJson(dynamic json) { +// statusCode = json['statusCode']; +// statusDescription = json['statusDescription']; +// data = json['data'] != null ? Data.fromJson(json['data']) : null; +// } +// +// int? statusCode; +// String? statusDescription; +// Data? data; +// +// Map toJson() { +// final map = {}; +// map['statusCode'] = statusCode; +// map['statusDescription'] = statusDescription; +// if (data != null) { +// map['data'] = data?.toJson(); +// } +// return map; +// } +// } +// +// class Data { +// Data({ +// this.staffMembers, +// this.totalSupervisionsCount, +// this.overdueSupervisionsCount, +// this.assignedSupervisionsCount, +// this.completedSupervisionsCount, +// this.count, +// this.offset, +// this.limit, +// }); +// +// Data.fromJson(dynamic json) { +// if (json['staffMembers'] != null) { +// staffMembers = []; +// json['staffMembers'].forEach((v) { +// staffMembers?.add(StaffMembers.fromJson(v)); +// }); +// } +// totalSupervisionsCount = json['totalSupervisionsCount']; +// overdueSupervisionsCount = json['overdueSupervisionsCount']; +// assignedSupervisionsCount = json['assignedSupervisionsCount']; +// completedSupervisionsCount = json['completedSupervisionsCount']; +// count = json['count']; +// offset = json['offset']; +// limit = json['limit']; +// } +// +// List? staffMembers; +// int? totalSupervisionsCount; +// int? overdueSupervisionsCount; +// int? assignedSupervisionsCount; +// int? completedSupervisionsCount; +// int? count; +// int? offset; +// int? limit; +// +// Map toJson() { +// final map = {}; +// if (staffMembers != null) { +// map['staffMembers'] = staffMembers?.map((v) => v.toJson()).toList(); +// } +// map['totalSupervisionsCount'] = totalSupervisionsCount; +// map['overdueSupervisionsCount'] = overdueSupervisionsCount; +// map['assignedSupervisionsCount'] = assignedSupervisionsCount; +// map['completedSupervisionsCount'] = completedSupervisionsCount; +// map['count'] = count; +// map['offset'] = offset; +// map['limit'] = limit; +// return map; +// } +// } +// +// class StaffMembers { +// StaffMembers({ +// this.contractedHours, +// this.id, +// this.staffMemberName, +// this.staffDesignation, +// this.staffOnLeave, +// this.holidays, +// this.complianceDocuments, +// this.niNumber, +// this.kin, +// this.user, +// this.managerId, +// this.clients, +// this.staffWorkLoads, +// this.staffHolidayRequests, +// this.staffTrainings, +// this.supervision, +// this.underSupervisions, +// this.staffWorkingDays, +// this.stafDob, +// this.active, +// this.covidCheck, +// this.createdAt, +// this.updatedAt, +// this.v, +// this.staffDisciplinaries, +// this.staffReferences, +// this.remainingHolidayHours, +// }); +// +// StaffMembers.fromJson(dynamic json) { +// contractedHours = json['contractedHours'] != null +// ? ContractedHours.fromJson(json['contractedHours']) +// : null; +// id = json['_id']; +// staffMemberName = json['staffMemberName']; +// staffDesignation = json['staffDesignation']; +// staffOnLeave = json['staffOnLeave']; +// holidays = List.castFrom(json['holidays'] ?? ""); +// complianceDocuments = +// List.castFrom(json['complianceDocuments'] ?? ""); +// niNumber = json['niNumber']; +// kin = json['kin']; +// user = json['user'] != null ? UserData.fromJson(json['user']) : null; +// managerId = +// json['managerId'] is Map ? UserData.fromJson(json['managerId']) : null; +// clients = List.castFrom(json['clients']); +// if (json['staffWorkLoads'] != null) { +// staffWorkLoads = []; +// json['staffWorkLoads'].forEach((v) { +// if (v is Map) { +// staffWorkLoads?.add(StaffWorkLoads.fromJson(v)); +// } +// }); +// } +// staffHolidayRequests = +// List.castFrom(json['staffHolidayRequests']); +// staffTrainings = List.castFrom(json['staffTrainings']); +// +// supervision = json['supervision'] != null +// ? Supervision.fromJson(json['supervision']) +// : null; +// underSupervisions = +// List.castFrom(json['underSupervisions']); +// staffWorkingDays = +// List.castFrom(json['staffWorkingDays']); +// stafDob = json['stafDob']; +// active = json['active']; +// covidCheck = json['covidCheck']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// staffDisciplinaries = json['staffDisciplinaries'] is Map +// ? StaffDisciplinaries.fromJson(json['staffDisciplinaries']) +// : null; +// staffReferences = json['staffReferences'] is Map +// ? StaffReferences.fromJson(json['staffReferences']) +// : null; +// remainingHolidayHours = json['remainingHolidayHours']; +// } +// +// ContractedHours? contractedHours; +// String? id; +// String? staffMemberName; +// String? staffDesignation; +// bool? staffOnLeave; +// List? holidays; +// List? complianceDocuments; +// String? niNumber; +// String? kin; +// UserData? user; +// UserData? managerId; +// List? clients; +// List? staffWorkLoads; +// List? staffHolidayRequests; +// List? staffTrainings; +// Supervision? supervision; +// List? underSupervisions; +// List? staffWorkingDays; +// String? stafDob; +// bool? active; +// bool? covidCheck; +// String? createdAt; +// String? updatedAt; +// int? v; +// StaffDisciplinaries? staffDisciplinaries; +// StaffReferences? staffReferences; +// int? remainingHolidayHours; +// +// Map toJson() { +// final map = {}; +// if (contractedHours != null) { +// map['contractedHours'] = contractedHours?.toJson(); +// } +// map['_id'] = id; +// map['staffMemberName'] = staffMemberName; +// map['staffDesignation'] = staffDesignation; +// map['staffOnLeave'] = staffOnLeave; +// if (holidays != null) { +// map['holidays'] = holidays?.map((v) => v.toJson()).toList(); +// } +// if (complianceDocuments != null) { +// map['complianceDocuments'] = +// complianceDocuments?.map((v) => v.toJson()).toList(); +// } +// map['niNumber'] = niNumber; +// map['kin'] = kin; +// if (user != null) { +// map['user'] = user?.toJson(); +// } +// if (managerId != null) { +// map['managerId'] = managerId?.toJson(); +// } +// if (clients != null) { +// map['clients'] = clients?.map((v) => v.toJson()).toList(); +// } +// if (staffWorkLoads != null) { +// map['staffWorkLoads'] = staffWorkLoads?.map((v) => v.toJson()).toList(); +// } +// if (staffHolidayRequests != null) { +// map['staffHolidayRequests'] = +// staffHolidayRequests?.map((v) => v.toJson()).toList(); +// } +// if (staffTrainings != null) { +// map['staffTrainings'] = staffTrainings?.map((v) => v.toJson()).toList(); +// } +// if (supervision != null) { +// map['supervision'] = supervision?.toJson(); +// } +// if (underSupervisions != null) { +// map['underSupervisions'] = +// underSupervisions?.map((v) => v.toJson()).toList(); +// } +// if (staffWorkingDays != null) { +// map['staffWorkingDays'] = +// staffWorkingDays?.map((v) => v.toJson()).toList(); +// } +// map['stafDob'] = stafDob; +// map['active'] = active; +// map['covidCheck'] = covidCheck; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// if (staffDisciplinaries != null) { +// map['staffDisciplinaries'] = staffDisciplinaries?.toJson(); +// } +// if (staffReferences != null) { +// map['staffReferences'] = staffReferences?.toJson(); +// } +// map['remainingHolidayHours'] = remainingHolidayHours; +// return map; +// } +// } +// +// class StaffReferences { +// StaffReferences({ +// this.id, +// this.docPaths, +// this.comments, +// this.staffMember, +// this.active, +// this.createdAt, +// this.updatedAt, +// this.v, +// }); +// +// StaffReferences.empty(); +// +// StaffReferences.fromJson(dynamic json) { +// id = json['_id']; +// docPaths = List.castFrom(json['docPaths']); +// comments = List.castFrom(json['comments']); +// staffMember = json['staffMember']; +// active = json['active']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// } +// +// String? id; +// List? docPaths; +// List? comments; +// String? staffMember; +// bool? active; +// String? createdAt; +// String? updatedAt; +// int? v; +// +// Map toJson() { +// final map = {}; +// map['_id'] = id; +// if (docPaths != null) { +// map['docPaths'] = docPaths?.map((v) => v.toJson()).toList(); +// } +// if (comments != null) { +// map['comments'] = comments?.map((v) => v.toJson()).toList(); +// } +// map['staffMember'] = staffMember; +// map['active'] = active; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// return map; +// } +// } +// +// class StaffDisciplinaries { +// StaffDisciplinaries({ +// this.id, +// this.docPaths, +// this.comments, +// this.staffMember, +// this.active, +// this.createdAt, +// this.updatedAt, +// this.v, +// }); +// +// StaffDisciplinaries.empty(); +// +// StaffDisciplinaries.fromJson(dynamic json) { +// id = json['_id']; +// docPaths = List.castFrom(json['docPaths']); +// comments = List.castFrom(json['comments']); +// staffMember = json['staffMember']; +// active = json['active']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// } +// +// String? id; +// List? docPaths; +// List? comments; +// String? staffMember; +// bool? active; +// String? createdAt; +// String? updatedAt; +// int? v; +// +// Map toJson() { +// final map = {}; +// map['_id'] = id; +// if (docPaths != null) { +// map['docPaths'] = docPaths?.map((v) => v.toJson()).toList(); +// } +// if (comments != null) { +// map['comments'] = comments?.map((v) => v.toJson()).toList(); +// } +// map['staffMember'] = staffMember; +// map['active'] = active; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// return map; +// } +// } +// +// class Supervision { +// Supervision({ +// this.supervisionName, +// this.sprDueDate, +// this.sprStatus, +// this.sprResult, +// this.templateTitleId, +// }); +// +// Supervision.fromJson(dynamic json) { +// supervisionName = json['supervisionName']; +// sprDueDate = json['sprDueDate']; +// sprStatus = json['sprStatus']; +// sprResult = json['sprResult']; +// templateTitleId = json['templateTitleId']; +// } +// +// String? supervisionName; +// String? sprDueDate; +// String? sprStatus; +// String? sprResult; +// String? templateTitleId; +// +// Map toJson() { +// final map = {}; +// map['supervisionName'] = supervisionName; +// map['sprDueDate'] = sprDueDate; +// map['sprStatus'] = sprStatus; +// map['sprResult'] = sprResult; +// map['templateTitleId'] = templateTitleId; +// return map; +// } +// } +// +// class StaffWorkLoads { +// StaffWorkLoads({ +// this.holidayEntitlement, +// this.id, +// this.isCurrentWrkLd, +// this.startDate, +// this.endDate, +// this.holidayAlwnNoOfDys, +// this.holidayAlwnNoOfHours, +// this.holidaysAvailed, +// this.holidaysRemaining, +// this.staffMember, +// this.carriedOverHours, +// this.active, +// this.createdAt, +// this.updatedAt, +// this.v, +// }); +// +// StaffWorkLoads.fromJson(dynamic json) { +// holidayEntitlement = json['holidayEntitlement'] != null +// ? HolidayEntitlement.fromJson(json['holidayEntitlement']) +// : null; +// id = json['_id']; +// isCurrentWrkLd = json['isCurrentWrkLd']; +// startDate = json['startDate']; +// endDate = json['endDate']; +// holidayAlwnNoOfDys = json['holidayAlwnNoOfDys']; +// holidayAlwnNoOfHours = json['holidayAlwnNoOfHours']; +// holidaysAvailed = json['holidaysAvailed']; +// holidaysRemaining = json['holidaysRemaining']; +// staffMember = json['staffMember']; +// carriedOverHours = json['carriedOverHours']; +// active = json['active']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// } +// +// HolidayEntitlement? holidayEntitlement; +// String? id; +// bool? isCurrentWrkLd; +// String? startDate; +// String? endDate; +// int? holidayAlwnNoOfDys; +// int? holidayAlwnNoOfHours; +// int? holidaysAvailed; +// int? holidaysRemaining; +// String? staffMember; +// int? carriedOverHours; +// bool? active; +// String? createdAt; +// String? updatedAt; +// int? v; +// +// Map toJson() { +// final map = {}; +// if (holidayEntitlement != null) { +// map['holidayEntitlement'] = holidayEntitlement?.toJson(); +// } +// map['_id'] = id; +// map['isCurrentWrkLd'] = isCurrentWrkLd; +// map['startDate'] = startDate; +// map['endDate'] = endDate; +// map['holidayAlwnNoOfDys'] = holidayAlwnNoOfDys; +// map['holidayAlwnNoOfHours'] = holidayAlwnNoOfHours; +// map['holidaysAvailed'] = holidaysAvailed; +// map['holidaysRemaining'] = holidaysRemaining; +// map['staffMember'] = staffMember; +// map['carriedOverHours'] = carriedOverHours; +// map['active'] = active; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// return map; +// } +// } +// +// class HolidayEntitlement { +// HolidayEntitlement({ +// this.numberOfDays, +// this.numberOfHours, +// this.numberOfWeeks, +// }); +// +// HolidayEntitlement.fromJson(dynamic json) { +// numberOfDays = json['numberOfDays']; +// numberOfHours = json['numberOfHours']; +// numberOfWeeks = json['numberOfWeeks']; +// } +// +// int? numberOfDays; +// int? numberOfHours; +// int? numberOfWeeks; +// +// Map toJson() { +// final map = {}; +// map['numberOfDays'] = numberOfDays; +// map['numberOfHours'] = numberOfHours; +// map['numberOfWeeks'] = numberOfWeeks; +// return map; +// } +// } +// +// class ManagerId { +// ManagerId({ +// this.fcmTokens, +// this.location, +// this.profileVideoUrl, +// this.id, +// this.userModelName, +// this.name, +// this.version, +// this.email, +// this.phoneNumber, +// this.active, +// this.role, +// this.profilePictureUrl, +// this.deviceId, +// this.verificationCode, +// this.isVerified, +// this.approved, +// this.blocked, +// this.createdAt, +// this.updatedAt, +// this.v, +// this.password, +// this.userSettings, +// this.modelId, +// }); +// +// ManagerId.fromJson(dynamic json) { +// fcmTokens = json['fcm_tokens'] != null +// ? FcmTokens.fromJson(json['fcm_tokens']) +// : null; +// location = json['location'] != null +// ? LocationData.fromJson(json['location']) +// : null; +// profileVideoUrl = json['profile_video_url']; +// id = json['_id']; +// userModelName = json['userModelName']; +// name = json['name']; +// version = json['version']; +// email = json['email']; +// phoneNumber = json['phoneNumber']; +// active = json['active']; +// role = json['role']; +// profilePictureUrl = json['profile_picture_url']; +// deviceId = json['deviceId']; +// verificationCode = json['verification_code']; +// isVerified = json['is_verified']; +// approved = json['approved']; +// blocked = json['blocked']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// password = json['password']; +// userSettings = json['userSettings']; +// modelId = json['modelId']; +// } +// +// FcmTokens? fcmTokens; +// LocationData? location; +// String? profileVideoUrl; +// String? id; +// String? userModelName; +// String? name; +// String? version; +// String? email; +// String? phoneNumber; +// bool? active; +// String? role; +// String? profilePictureUrl; +// String? deviceId; +// String? verificationCode; +// bool? isVerified; +// bool? approved; +// bool? blocked; +// String? createdAt; +// String? updatedAt; +// int? v; +// String? password; +// String? userSettings; +// String? modelId; +// +// Map toJson() { +// final map = {}; +// if (fcmTokens != null) { +// map['fcm_tokens'] = fcmTokens?.toJson(); +// } +// if (location != null) { +// map['location'] = location?.toJson(); +// } +// map['profile_video_url'] = profileVideoUrl; +// map['_id'] = id; +// map['userModelName'] = userModelName; +// map['name'] = name; +// map['version'] = version; +// map['email'] = email; +// map['phoneNumber'] = phoneNumber; +// map['active'] = active; +// map['role'] = role; +// map['profile_picture_url'] = profilePictureUrl; +// map['deviceId'] = deviceId; +// map['verification_code'] = verificationCode; +// map['is_verified'] = isVerified; +// map['approved'] = approved; +// map['blocked'] = blocked; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// map['password'] = password; +// map['userSettings'] = userSettings; +// map['modelId'] = modelId; +// return map; +// } +// } +// +// // class Location { +// // Location({ +// // this.type, +// // this.coordinates,}); +// // +// // Location.fromJson(dynamic json) { +// // type = json['type']; +// // coordinates = json['coordinates'] != null ? json['coordinates'].cast() : []; +// // } +// // String? type; +// // List? coordinates; +// // +// // Map toJson() { +// // final map = {}; +// // map['type'] = type; +// // map['coordinates'] = coordinates; +// // return map; +// // } +// // +// // } +// +// // class FcmTokens { +// // FcmTokens({ +// // this.token, +// // this.deviceType,}); +// // +// // FcmTokens.fromJson(dynamic json) { +// // token = json['token']; +// // deviceType = json['deviceType']; +// // } +// // String? token; +// // String? deviceType; +// // +// // Map toJson() { +// // final map = {}; +// // map['token'] = token; +// // map['deviceType'] = deviceType; +// // return map; +// // } +// // +// // } +// +// // class User { +// // User({ +// // this.fcmTokens, +// // this.location, +// // this.id, +// // this.userModelName, +// // this.name, +// // this.version, +// // this.email, +// // this.phoneNumber, +// // this.active, +// // this.role, +// // this.profilePictureUrl, +// // this.profileVideoUrl, +// // this.deviceId, +// // this.verificationCode, +// // this.isVerified, +// // this.approved, +// // this.blocked, +// // this.createdAt, +// // this.updatedAt, +// // this.v, +// // this.password, +// // this.userSettings, +// // this.modelId,}); +// // +// // User.fromJson(dynamic json) { +// // fcmTokens = json['fcm_tokens'] != null ? FcmTokens.fromJson(json['fcm_tokens']) : null; +// // location = json['location'] != null ? LocationData.fromJson(json['location']) : null; +// // id = json['_id']; +// // userModelName = json['userModelName']; +// // name = json['name']; +// // version = json['version']; +// // email = json['email']; +// // phoneNumber = json['phoneNumber']; +// // active = json['active']; +// // role = json['role']; +// // profilePictureUrl = json['profile_picture_url']; +// // profileVideoUrl = json['profile_video_url']; +// // deviceId = json['deviceId']; +// // verificationCode = json['verification_code']; +// // isVerified = json['is_verified']; +// // approved = json['approved']; +// // blocked = json['blocked']; +// // createdAt = json['createdAt']; +// // updatedAt = json['updatedAt']; +// // v = json['__v']; +// // password = json['password']; +// // userSettings = json['userSettings']; +// // modelId = json['modelId']; +// // } +// // FcmTokens? fcmTokens; +// // LocationData? location; +// // String? id; +// // String? userModelName; +// // String? name; +// // String? version; +// // String? email; +// // String? phoneNumber; +// // bool? active; +// // String? role; +// // String? profilePictureUrl; +// // String? profileVideoUrl; +// // String? deviceId; +// // String? verificationCode; +// // bool? isVerified; +// // bool? approved; +// // bool? blocked; +// // String? createdAt; +// // String? updatedAt; +// // int? v; +// // String? password; +// // String? userSettings; +// // String? modelId; +// // +// // Map toJson() { +// // final map = {}; +// // if (fcmTokens != null) { +// // map['fcm_tokens'] = fcmTokens?.toJson(); +// // } +// // if (location != null) { +// // map['location'] = location?.toJson(); +// // } +// // map['_id'] = id; +// // map['userModelName'] = userModelName; +// // map['name'] = name; +// // map['version'] = version; +// // map['email'] = email; +// // map['phoneNumber'] = phoneNumber; +// // map['active'] = active; +// // map['role'] = role; +// // map['profile_picture_url'] = profilePictureUrl; +// // map['profile_video_url'] = profileVideoUrl; +// // map['deviceId'] = deviceId; +// // map['verification_code'] = verificationCode; +// // map['is_verified'] = isVerified; +// // map['approved'] = approved; +// // map['blocked'] = blocked; +// // map['createdAt'] = createdAt; +// // map['updatedAt'] = updatedAt; +// // map['__v'] = v; +// // map['password'] = password; +// // map['userSettings'] = userSettings; +// // map['modelId'] = modelId; +// // return map; +// // } +// // +// // } +// +// class ContractedHours { +// ContractedHours({ +// this.contractedHours, +// this.totalShiftHoursWeek, +// this.noOfShifts, +// }); +// +// ContractedHours.fromJson(dynamic json) { +// contractedHours = json['contractedHours']; +// totalShiftHoursWeek = json['totalShiftHoursWeek']; +// noOfShifts = json['noOfShifts']; +// } +// +// int? contractedHours; +// int? totalShiftHoursWeek; +// int? noOfShifts; +// +// Map toJson() { +// final map = {}; +// map['contractedHours'] = contractedHours; +// map['totalShiftHoursWeek'] = totalShiftHoursWeek; +// map['noOfShifts'] = noOfShifts; +// return map; +// } +// } diff --git a/lib/models/profileData/user_data.dart b/lib/models/profileData/user_data.dart new file mode 100644 index 0000000..606c9d1 --- /dev/null +++ b/lib/models/profileData/user_data.dart @@ -0,0 +1,108 @@ +import '../clients/service_users_model.dart'; +import 'LocationData.dart'; + +class UserData { + UserData({ + this.location, + this.id, + this.userModelName, + this.name, + this.version, + this.email, + this.phoneNumber, + this.active, + this.role, + this.profilePictureUrl, + this.profileVideoUrl, + this.deviceId, + this.verificationCode, + this.isVerified, + this.approved, + this.blocked, + this.createdAt, + this.updatedAt, + this.userSettings, + this.modelId, + }); + + UserData.fromJson(dynamic json) { + location = json['location'] is Map + ? LocationData.fromJson(json['location']) + : null; + id = json['_id']; + userModelName = json['userModelName']; + name = json['name']; + version = json['version']; + email = json['email']; + phoneNumber = json['phoneNumber']; + active = json['active']; + role = json['role']; + profilePictureUrl = json['profile_picture_url']; + profileVideoUrl = json['profile_video_url']; + deviceId = json['deviceId']; + verificationCode = json['verification_code']; + isVerified = json['is_verified']; + approved = json['approved']; + blocked = json['blocked']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + userSettings = json['userSettings'] is String ? json['userSettings'] : null; + modelId = json['modelId'] is Map + ? ServiceUserModel.fromJson(json['modelId']) + : null; + } + + LocationData? location; + String? id; + String? userModelName; + String? name; + String? version; + String? email; + String? phoneNumber; + bool? active; + String? role; + String? profilePictureUrl; + String? profileVideoUrl; + String? deviceId; + String? verificationCode; + bool? isVerified; + bool? approved; + bool? blocked; + String? createdAt; + String? updatedAt; + String? userSettings; + ServiceUserModel? modelId; + + String get displayName => + (_fullName.trim().isNotEmpty) ? _fullName.trim() : (name ?? ""); + + String get _fullName => + "${modelId?.suFirstMiddleName ?? ""} ${modelId?.suLastName ?? ""}"; + + Map toJson() { + final map = {}; + if (location != null) { + map['location'] = location?.toJson(); + } + map['_id'] = id; + map['userModelName'] = userModelName; + map['name'] = name; + map['version'] = version; + map['email'] = email; + map['phoneNumber'] = phoneNumber; + map['active'] = active; + map['role'] = role; + map['profile_picture_url'] = profilePictureUrl; + map['profile_video_url'] = profileVideoUrl; + map['deviceId'] = deviceId; + map['verification_code'] = verificationCode; + map['is_verified'] = isVerified; + map['approved'] = approved; + map['blocked'] = blocked; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + map['userSettings'] = userSettings; + map['modelId'] = modelId; + return map; + } +} diff --git a/lib/models/profile_screen_model.dart b/lib/models/profile_screen_model.dart new file mode 100644 index 0000000..1811146 --- /dev/null +++ b/lib/models/profile_screen_model.dart @@ -0,0 +1,670 @@ +import 'package:ftc_mobile_app/models/staffWorkload/StaffWorkloadResponse.dart'; + +import 'profileData/user_data.dart'; + +class ProfileDataModel { + ProfileDataModel({ + this.statusCode, + this.statusDescription, + this.data, + }); + + ProfileDataModel.fromJson(dynamic json) { + statusCode = json['statusCode']; + statusDescription = json['statusDescription']; + data = json['data'] != null ? Data.fromJson(json['data']) : null; + } + + int? statusCode; + String? statusDescription; + Data? data; + + Map toJson() { + final map = {}; + map['statusCode'] = statusCode; + map['statusDescription'] = statusDescription; + if (data != null) { + map['data'] = data?.toJson(); + } + return map; + } +} + +class Data { + Data({ + this.staffMembers, + this.totalSupervisionsCount, + this.overdueSupervisionsCount, + this.assignedSupervisionsCount, + this.completedSupervisionsCount, + this.count, + this.offset, + this.limit, + }); + + Data.fromJson(dynamic json) { + if (json['staffMembers'] != null) { + staffMembers = []; + json['staffMembers'].forEach((v) { + staffMembers?.add(StaffMembers.fromJson(v)); + }); + } + totalSupervisionsCount = json['totalSupervisionsCount']; + overdueSupervisionsCount = json['overdueSupervisionsCount']; + assignedSupervisionsCount = json['assignedSupervisionsCount']; + completedSupervisionsCount = json['completedSupervisionsCount']; + count = json['count']; + offset = json['offset']; + limit = json['limit']; + } + + List? staffMembers; + int? totalSupervisionsCount; + int? overdueSupervisionsCount; + int? assignedSupervisionsCount; + int? completedSupervisionsCount; + int? count; + int? offset; + int? limit; + + Map toJson() { + final map = {}; + if (staffMembers != null) { + map['staffMembers'] = staffMembers?.map((v) => v.toJson()).toList(); + } + map['totalSupervisionsCount'] = totalSupervisionsCount; + map['overdueSupervisionsCount'] = overdueSupervisionsCount; + map['assignedSupervisionsCount'] = assignedSupervisionsCount; + map['completedSupervisionsCount'] = completedSupervisionsCount; + map['count'] = count; + map['offset'] = offset; + map['limit'] = limit; + return map; + } +} + +class StaffMembers { + StaffMembers({ + this.contractedHours, + this.id, + this.staffMemberName, + this.staffDesignation, + this.staffOnLeave, + this.holidays, + this.complianceDocuments, + this.niNumber, + this.kin, + this.user, + this.managerId, + this.clients, + this.staffWorkLoads, + this.staffHolidayRequests, + this.staffTrainings, + this.supervision, + this.underSupervisions, + this.staffWorkingDays, + this.stafDob, + this.active, + this.covidCheck, + this.createdAt, + this.updatedAt, + this.staffDisciplinaries, + this.staffReferences, + this.remainingHolidayHours, + }); + + StaffMembers.fromJson(dynamic json) { + contractedHours = json['contractedHours'] != null + ? ContractedHours.fromJson(json['contractedHours']) + : null; + id = json['_id']; + staffMemberName = json['staffMemberName']; + staffDesignation = json['staffDesignation']; + staffOnLeave = json['staffOnLeave']; + holidays = List.castFrom(json['holidays'] ?? ""); + complianceDocuments = + List.castFrom(json['complianceDocuments'] ?? ""); + niNumber = json['niNumber']; + kin = json['kin']; + user = json['user'] is Map ? UserData.fromJson(json['user']) : null; + managerId = + json['managerId'] is Map ? UserData.fromJson(json['managerId']) : null; + clients = List.castFrom(json['clients']); + if (json['staffWorkLoads'] is List) { + staffWorkLoads = []; + json['staffWorkLoads'].forEach((v) { + if (v is Map) { + staffWorkLoads?.add(StaffWorkLoads.fromJson(v)); + } + }); + } + staffHolidayRequests = + List.castFrom(json['staffHolidayRequests']); + staffTrainings = List.castFrom(json['staffTrainings']); + + supervision = json['supervision'] != null + ? Supervision.fromJson(json['supervision']) + : null; + underSupervisions = + List.castFrom(json['underSupervisions']); + staffWorkingDays = + List.castFrom(json['staffWorkingDays']); + stafDob = json['stafDob']; + active = json['active']; + covidCheck = json['covidCheck']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + staffDisciplinaries = json['staffDisciplinaries'] is Map + ? StaffDisciplinaries.fromJson(json['staffDisciplinaries']) + : null; + staffReferences = json['staffReferences'] is Map + ? StaffReferences.fromJson(json['staffReferences']) + : null; + remainingHolidayHours = json['remainingHolidayHours']; + } + + ContractedHours? contractedHours; + String? id; + String? staffMemberName; + String? staffDesignation; + bool? staffOnLeave; + List? holidays; + List? complianceDocuments; + String? niNumber; + String? kin; + UserData? user; + UserData? managerId; + List? clients; + List? staffWorkLoads; + List? staffHolidayRequests; + List? staffTrainings; + Supervision? supervision; + List? underSupervisions; + List? staffWorkingDays; + String? stafDob; + bool? active; + bool? covidCheck; + String? createdAt; + String? updatedAt; + StaffDisciplinaries? staffDisciplinaries; + StaffReferences? staffReferences; + int? remainingHolidayHours; + + Map toJson() { + final map = {}; + if (contractedHours != null) { + map['contractedHours'] = contractedHours?.toJson(); + } + map['_id'] = id; + map['staffMemberName'] = staffMemberName; + map['staffDesignation'] = staffDesignation; + map['staffOnLeave'] = staffOnLeave; + if (holidays != null) { + map['holidays'] = holidays?.map((v) => v.toJson()).toList(); + } + if (complianceDocuments != null) { + map['complianceDocuments'] = + complianceDocuments?.map((v) => v.toJson()).toList(); + } + map['niNumber'] = niNumber; + map['kin'] = kin; + if (user != null) { + map['user'] = user?.toJson(); + } + if (managerId != null) { + map['managerId'] = managerId?.toJson(); + } + if (clients != null) { + map['clients'] = clients?.map((v) => v.toJson()).toList(); + } + if (staffWorkLoads != null) { + map['staffWorkLoads'] = staffWorkLoads?.map((v) => v.toJson()).toList(); + } + if (staffHolidayRequests != null) { + map['staffHolidayRequests'] = + staffHolidayRequests?.map((v) => v.toJson()).toList(); + } + if (staffTrainings != null) { + map['staffTrainings'] = staffTrainings?.map((v) => v.toJson()).toList(); + } + if (supervision != null) { + map['supervision'] = supervision?.toJson(); + } + if (underSupervisions != null) { + map['underSupervisions'] = + underSupervisions?.map((v) => v.toJson()).toList(); + } + if (staffWorkingDays != null) { + map['staffWorkingDays'] = + staffWorkingDays?.map((v) => v.toJson()).toList(); + } + map['stafDob'] = stafDob; + map['active'] = active; + map['covidCheck'] = covidCheck; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + if (staffDisciplinaries != null) { + map['staffDisciplinaries'] = staffDisciplinaries?.toJson(); + } + if (staffReferences != null) { + map['staffReferences'] = staffReferences?.toJson(); + } + map['remainingHolidayHours'] = remainingHolidayHours; + return map; + } +} + +class StaffReferences { + StaffReferences({ + this.id, + this.docPaths, + this.comments, + this.staffMember, + this.active, + this.createdAt, + this.updatedAt, + this.v, + }); + + StaffReferences.empty(); + + StaffReferences.fromJson(dynamic json) { + id = json['_id']; + docPaths = List.castFrom(json['docPaths']); + comments = List.castFrom(json['comments']); + staffMember = json['staffMember']; + active = json['active']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + v = json['__v']; + } + + String? id; + List? docPaths; + List? comments; + String? staffMember; + bool? active; + String? createdAt; + String? updatedAt; + int? v; + + Map toJson() { + final map = {}; + map['_id'] = id; + if (docPaths != null) { + map['docPaths'] = docPaths?.map((v) => v.toJson()).toList(); + } + if (comments != null) { + map['comments'] = comments?.map((v) => v.toJson()).toList(); + } + map['staffMember'] = staffMember; + map['active'] = active; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + map['__v'] = v; + return map; + } +} + +class StaffDisciplinaries { + StaffDisciplinaries({ + this.id, + this.docPaths, + this.comments, + this.staffMember, + this.active, + this.createdAt, + this.updatedAt, + this.v, + }); + + StaffDisciplinaries.empty(); + + StaffDisciplinaries.fromJson(dynamic json) { + id = json['_id']; + docPaths = List.castFrom(json['docPaths']); + comments = List.castFrom(json['comments']); + staffMember = json['staffMember']; + active = json['active']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + v = json['__v']; + } + + String? id; + List? docPaths; + List? comments; + String? staffMember; + bool? active; + String? createdAt; + String? updatedAt; + int? v; + + Map toJson() { + final map = {}; + map['_id'] = id; + if (docPaths != null) { + map['docPaths'] = docPaths?.map((v) => v.toJson()).toList(); + } + if (comments != null) { + map['comments'] = comments?.map((v) => v.toJson()).toList(); + } + map['staffMember'] = staffMember; + map['active'] = active; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + map['__v'] = v; + return map; + } +} + +class Supervision { + Supervision({ + this.supervisionName, + this.sprDueDate, + this.sprStatus, + this.sprResult, + this.templateTitleId, + }); + + Supervision.fromJson(dynamic json) { + supervisionName = json['supervisionName']; + sprDueDate = json['sprDueDate']; + sprStatus = json['sprStatus']; + sprResult = json['sprResult']; + templateTitleId = json['templateTitleId']; + } + + String? supervisionName; + String? sprDueDate; + String? sprStatus; + String? sprResult; + String? templateTitleId; + + Map toJson() { + final map = {}; + map['supervisionName'] = supervisionName; + map['sprDueDate'] = sprDueDate; + map['sprStatus'] = sprStatus; + map['sprResult'] = sprResult; + map['templateTitleId'] = templateTitleId; + return map; + } +} + +// class StaffWorkLoads { +// StaffWorkLoads({ +// this.holidayEntitlement, +// this.id, +// this.isCurrentWrkLd, +// this.startDate, +// this.endDate, +// this.holidayAlwnNoOfDys, +// this.holidayAlwnNoOfHours, +// this.holidaysAvailed, +// this.holidaysRemaining, +// this.staffMember, +// this.carriedOverHours, +// this.active, +// this.createdAt, +// this.updatedAt, +// this.v,}); +// +// StaffWorkLoads.fromJson(dynamic json) { +// holidayEntitlement = json['holidayEntitlement'] != null ? HolidayEntitlement.fromJson(json['holidayEntitlement']) : null; +// id = json['_id']; +// isCurrentWrkLd = json['isCurrentWrkLd']; +// startDate = json['startDate']; +// endDate = json['endDate']; +// holidayAlwnNoOfDys = json['holidayAlwnNoOfDys']; +// holidayAlwnNoOfHours = json['holidayAlwnNoOfHours']; +// holidaysAvailed = json['holidaysAvailed']; +// holidaysRemaining = json['holidaysRemaining']; +// staffMember = json['staffMember']; +// carriedOverHours = json['carriedOverHours']; +// active = json['active']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// } +// HolidayEntitlement? holidayEntitlement; +// String? id; +// bool? isCurrentWrkLd; +// String? startDate; +// String? endDate; +// int? holidayAlwnNoOfDys; +// int? holidayAlwnNoOfHours; +// int? holidaysAvailed; +// int? holidaysRemaining; +// String? staffMember; +// int? carriedOverHours; +// bool? active; +// String? createdAt; +// String? updatedAt; +// int? v; +// +// Map toJson() { +// final map = {}; +// if (holidayEntitlement != null) { +// map['holidayEntitlement'] = holidayEntitlement?.toJson(); +// } +// map['_id'] = id; +// map['isCurrentWrkLd'] = isCurrentWrkLd; +// map['startDate'] = startDate; +// map['endDate'] = endDate; +// map['holidayAlwnNoOfDys'] = holidayAlwnNoOfDys; +// map['holidayAlwnNoOfHours'] = holidayAlwnNoOfHours; +// map['holidaysAvailed'] = holidaysAvailed; +// map['holidaysRemaining'] = holidaysRemaining; +// map['staffMember'] = staffMember; +// map['carriedOverHours'] = carriedOverHours; +// map['active'] = active; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// return map; +// } +// +// } +// +// class HolidayEntitlement { +// HolidayEntitlement({ +// this.numberOfDays, +// this.numberOfHours, +// this.numberOfWeeks,}); +// +// HolidayEntitlement.fromJson(dynamic json) { +// numberOfDays = json['numberOfDays']; +// numberOfHours = json['numberOfHours']; +// numberOfWeeks = json['numberOfWeeks']; +// } +// int? numberOfDays; +// int? numberOfHours; +// int? numberOfWeeks; +// +// Map toJson() { +// final map = {}; +// map['numberOfDays'] = numberOfDays; +// map['numberOfHours'] = numberOfHours; +// map['numberOfWeeks'] = numberOfWeeks; +// return map; +// } +// +// } + +// class ManagerId { +// ManagerId({ +// this.fcmTokens, +// this.location, +// this.profileVideoUrl, +// this.id, +// this.userModelName, +// this.name, +// this.version, +// this.email, +// this.phoneNumber, +// this.active, +// this.role, +// this.profilePictureUrl, +// this.deviceId, +// this.verificationCode, +// this.isVerified, +// this.approved, +// this.blocked, +// this.createdAt, +// this.updatedAt, +// this.v, +// this.password, +// this.userSettings, +// this.modelId,}); +// +// ManagerId.fromJson(dynamic json) { +// fcmTokens = json['fcm_tokens'] != null ? FcmTokens.fromJson(json['fcm_tokens']) : null; +// location = json['location'] != null ? LocationData.fromJson(json['location']) : null; +// profileVideoUrl = json['profile_video_url']; +// id = json['_id']; +// userModelName = json['userModelName']; +// name = json['name']; +// version = json['version']; +// email = json['email']; +// phoneNumber = json['phoneNumber']; +// active = json['active']; +// role = json['role']; +// profilePictureUrl = json['profile_picture_url']; +// deviceId = json['deviceId']; +// verificationCode = json['verification_code']; +// isVerified = json['is_verified']; +// approved = json['approved']; +// blocked = json['blocked']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// password = json['password']; +// userSettings = json['userSettings']; +// modelId = json['modelId']; +// } +// FcmTokens? fcmTokens; +// LocationData? location; +// String? profileVideoUrl; +// String? id; +// String? userModelName; +// String? name; +// String? version; +// String? email; +// String? phoneNumber; +// bool? active; +// String? role; +// String? profilePictureUrl; +// String? deviceId; +// String? verificationCode; +// bool? isVerified; +// bool? approved; +// bool? blocked; +// String? createdAt; +// String? updatedAt; +// int? v; +// String? password; +// String? userSettings; +// String? modelId; +// +// Map toJson() { +// final map = {}; +// if (fcmTokens != null) { +// map['fcm_tokens'] = fcmTokens?.toJson(); +// } +// if (location != null) { +// map['location'] = location?.toJson(); +// } +// map['profile_video_url'] = profileVideoUrl; +// map['_id'] = id; +// map['userModelName'] = userModelName; +// map['name'] = name; +// map['version'] = version; +// map['email'] = email; +// map['phoneNumber'] = phoneNumber; +// map['active'] = active; +// map['role'] = role; +// map['profile_picture_url'] = profilePictureUrl; +// map['deviceId'] = deviceId; +// map['verification_code'] = verificationCode; +// map['is_verified'] = isVerified; +// map['approved'] = approved; +// map['blocked'] = blocked; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// map['password'] = password; +// map['userSettings'] = userSettings; +// map['modelId'] = modelId; +// return map; +// } +// +// } + +// class Location { +// Location({ +// this.type, +// this.coordinates,}); +// +// Location.fromJson(dynamic json) { +// type = json['type']; +// coordinates = json['coordinates'] != null ? json['coordinates'].cast() : []; +// } +// String? type; +// List? coordinates; +// +// Map toJson() { +// final map = {}; +// map['type'] = type; +// map['coordinates'] = coordinates; +// return map; +// } +// +// } + +// class FcmTokens { +// FcmTokens({ +// this.token, +// this.deviceType,}); +// +// FcmTokens.fromJson(dynamic json) { +// token = json['token']; +// deviceType = json['deviceType']; +// } +// String? token; +// String? deviceType; +// +// Map toJson() { +// final map = {}; +// map['token'] = token; +// map['deviceType'] = deviceType; +// return map; +// } +// +// } + +class ContractedHours { + ContractedHours({ + this.contractedHours, + this.totalShiftHoursWeek, + this.noOfShifts, + }); + + ContractedHours.fromJson(dynamic json) { + contractedHours = json['contractedHours']; + totalShiftHoursWeek = json['totalShiftHoursWeek']; + noOfShifts = json['noOfShifts']; + } + + int? contractedHours; + int? totalShiftHoursWeek; + int? noOfShifts; + + Map toJson() { + final map = {}; + map['contractedHours'] = contractedHours; + map['totalShiftHoursWeek'] = totalShiftHoursWeek; + map['noOfShifts'] = noOfShifts; + return map; + } +} diff --git a/lib/models/requests/HolidayRequestData.dart b/lib/models/requests/HolidayRequestData.dart new file mode 100644 index 0000000..2118b9c --- /dev/null +++ b/lib/models/requests/HolidayRequestData.dart @@ -0,0 +1,41 @@ +class HolidayRequestData { + HolidayRequestData({ + this.hldRqStartDate, + this.hldRqEndDate, + this.hldRqTotalDays, + this.hldRqTotalHours, + this.hldRqStatus, + this.hldRequestType, + this.staffRequester, + }); + + HolidayRequestData.fromJson(dynamic json) { + hldRqStartDate = json['hldRqStartDate']; + hldRqEndDate = json['hldRqEndDate']; + hldRqTotalDays = json['hldRqTotalDays']; + hldRqTotalHours = json['hldRqTotalHours']; + hldRqStatus = json['hldRqStatus']; + hldRequestType = json['hldRequestType']; + staffRequester = json['staffRequester']; + } + + int? hldRqStartDate; + int? hldRqEndDate; + int? hldRqTotalDays; + int? hldRqTotalHours; + String? hldRqStatus; + String? hldRequestType; + String? staffRequester; + + Map toJson() { + final map = {}; + map['hldRqStartDate'] = hldRqStartDate; + map['hldRqEndDate'] = hldRqEndDate; + map['hldRqTotalDays'] = hldRqTotalDays; + map['hldRqTotalHours'] = hldRqTotalHours; + map['hldRqStatus'] = hldRqStatus; + map['hldRequestType'] = hldRequestType; + map['staffRequester'] = staffRequester; + return map; + } +} diff --git a/lib/models/response_model.dart b/lib/models/response_model.dart new file mode 100644 index 0000000..9eac12e --- /dev/null +++ b/lib/models/response_model.dart @@ -0,0 +1,39 @@ +class ResponseModel{ + int statusCode = -1; + String statusDescription = ""; + dynamic data =""; + String userToken = ""; + Map header = {}; + + ResponseModel(); + + ResponseModel.named({ + required this.statusCode, + required this.statusDescription, + this.data, + }); + + ResponseModel.fromJson(Map json, {this.statusCode = 0}) { + statusDescription = json["message"]??""; + data = json["data"] ?? json; + userToken = json["token"] ?? ""; + } + + ResponseModel.errorFromJson(Map json,{this.statusCode = 0}){ + statusDescription = json['error'] ?? ''; + data = json["data"] ?? json; + } + + Map toJson() { + return { + 'statusCode': statusCode, + 'statusDescription': statusDescription, + 'data': data, + }; + } + + @override + String toString() { + return 'ResponseModel{statusCode: $statusCode, statusDescription: $statusDescription, data: $data, userToken: $userToken, header: $header}'; + } +} \ No newline at end of file diff --git a/lib/models/rota/Days.dart b/lib/models/rota/Days.dart new file mode 100644 index 0000000..cfc3682 --- /dev/null +++ b/lib/models/rota/Days.dart @@ -0,0 +1,62 @@ +import '../profileData/user_data.dart'; + +class Days { + Days({ + this.day, + this.shiftStartTime, + this.shiftEndTime, + this.workHrs, + this.patientId, + this.isSleepOver, + this.isOverNightStay, + this.addedby, + this.note, + this.id, + this.templateId, + }); + + Days.fromJson(dynamic json) { + day = json['day']; + shiftStartTime = json['shiftStartTime']; + shiftEndTime = json['shiftEndTime']; + workHrs = json['workHrs']; + patientId = + json['patientId'] != null ? UserData.fromJson(json['patientId']) : null; + isSleepOver = json['isSleepOver']; + isOverNightStay = json['isOverNightStay']; + addedby = json['addedby']; + note = json['note']; + id = json['_id']; + templateId = json['templateId']; + } + + String? day; + int? shiftStartTime; + int? shiftEndTime; + String? workHrs; + UserData? patientId; + bool? isSleepOver; + bool? isOverNightStay; + String? addedby; + String? note; + String? id; + String? templateId; + + Map toJson() { + final map = {}; + map['day'] = day; + map['shiftStartTime'] = shiftStartTime; + map['shiftEndTime'] = shiftEndTime; + map['workHrs'] = workHrs; + if (patientId != null) { + map['patientId'] = patientId?.toJson(); + } + map['isSleepOver'] = isSleepOver; + map['isOverNightStay'] = isOverNightStay; + map['addedby'] = addedby; + map['note'] = note; + map['_id'] = id; + map['templateId'] = templateId; + return map; + } +} diff --git a/lib/models/rota/Diagnosises.dart b/lib/models/rota/Diagnosises.dart new file mode 100644 index 0000000..6cbbeff --- /dev/null +++ b/lib/models/rota/Diagnosises.dart @@ -0,0 +1,32 @@ +class Diagnosises { + Diagnosises({ + this.diagnosisText, + this.diagnosisDate, + this.diagnosisBy, + this.isCurrentDiagnosis, + this.id,}); + + Diagnosises.fromJson(dynamic json) { + diagnosisText = json['diagnosisText']; + diagnosisDate = json['diagnosisDate']; + diagnosisBy = json['diagnosisBy']; + isCurrentDiagnosis = json['isCurrentDiagnosis']; + id = json['_id']; + } + String? diagnosisText; + String? diagnosisDate; + String? diagnosisBy; + bool? isCurrentDiagnosis; + String? id; + + Map toJson() { + final map = {}; + map['diagnosisText'] = diagnosisText; + map['diagnosisDate'] = diagnosisDate; + map['diagnosisBy'] = diagnosisBy; + map['isCurrentDiagnosis'] = isCurrentDiagnosis; + map['_id'] = id; + return map; + } + +} \ No newline at end of file diff --git a/lib/models/rota/LiveRoasterResponseData.dart b/lib/models/rota/LiveRoasterResponseData.dart new file mode 100644 index 0000000..aa56389 --- /dev/null +++ b/lib/models/rota/LiveRoasterResponseData.dart @@ -0,0 +1,27 @@ +import 'WeekArrayData.dart'; + +class LiveRoasterResponseData { + LiveRoasterResponseData({ + this.daysArray, + }); + + LiveRoasterResponseData.fromJson(dynamic json) { + if (json['data'] != null && json['data'] is List) { + daysArray = []; + json['data'].forEach((v) { + daysArray?.add(DaysArrayData.fromJson(v)); + }); + } + } + + // List? liveRoster; + List? daysArray; + + Map toJson() { + final map = {}; + if (daysArray != null) { + map['data'] = daysArray?.map((v) => v.toJson()).toList(); + } + return map; + } +} diff --git a/lib/models/rota/LiveRoster.dart b/lib/models/rota/LiveRoster.dart new file mode 100644 index 0000000..2af5982 --- /dev/null +++ b/lib/models/rota/LiveRoster.dart @@ -0,0 +1,65 @@ +import 'ShiftLocation.dart'; + +class LiveRoster { + LiveRoster({ + this.id, + this.rotaTemplateId, + this.rosterStartDate, + this.rosterEndDate, + this.templateWeeks, + this.shiftLocation, + this.active, + this.lastModifiedBy, + this.createdAt, + this.updatedAt, + this.v,}); + + LiveRoster.fromJson(dynamic json) { + id = json['_id']; + rotaTemplateId = json['rotaTemplateId']; + rosterStartDate = json['rosterStartDate']; + rosterEndDate = json['rosterEndDate']; + templateWeeks = json['templateWeeks']; + if (json['shiftLocation'] != null && json['shiftLocation'] is List) { + shiftLocation = []; + json['shiftLocation'].forEach((v) { + shiftLocation?.add(ShiftLocation.fromJson(v)); + }); + } + active = json['active']; + lastModifiedBy = json['lastModifiedBy']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + v = json['__v']; + } + String? id; + String? rotaTemplateId; + int? rosterStartDate; + int? rosterEndDate; + int? templateWeeks; + List? shiftLocation; + bool? active; + String? lastModifiedBy; + String? createdAt; + String? updatedAt; + int? v; + + Map toJson() { + final map = {}; + map['_id'] = id; + map['rotaTemplateId'] = rotaTemplateId; + map['rosterStartDate'] = rosterStartDate; + map['rosterEndDate'] = rosterEndDate; + map['templateWeeks'] = templateWeeks; + if (shiftLocation != null) { + map['shiftLocation'] = shiftLocation?.map((v) => v.toJson()).toList(); + } + map['active'] = active; + map['lastModifiedBy'] = lastModifiedBy; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + map['__v'] = v; + return map; + } + +} \ No newline at end of file diff --git a/lib/models/rota/Live_roaster_response.dart b/lib/models/rota/Live_roaster_response.dart new file mode 100644 index 0000000..3f5ebbc --- /dev/null +++ b/lib/models/rota/Live_roaster_response.dart @@ -0,0 +1,28 @@ +import 'LiveRoasterResponseData.dart'; + +class LiveRoasterResponse { + LiveRoasterResponse({ + this.status, + this.message, + this.data,}); + + LiveRoasterResponse.fromJson(dynamic json) { + status = json['status']; + message = json['message']; + data = json['data'] != null ? LiveRoasterResponseData.fromJson(json['data']) : null; + } + String? status; + String? message; + LiveRoasterResponseData? data; + + Map toJson() { + final map = {}; + map['status'] = status; + map['message'] = message; + if (data != null) { + map['data'] = data?.toJson(); + } + return map; + } + +} \ No newline at end of file diff --git a/lib/models/rota/ShiftArray.dart b/lib/models/rota/ShiftArray.dart new file mode 100644 index 0000000..77afaef --- /dev/null +++ b/lib/models/rota/ShiftArray.dart @@ -0,0 +1,113 @@ +import 'package:ftc_mobile_app/models/profile_screen_model.dart'; +import '../profileData/user_data.dart'; +import 'LiveRoster.dart'; +import 'Days.dart'; +import 'StaffHolidays.dart'; + +class ShiftArray { + ShiftArray({ + this.liveRoster, + this.staffHolidays, + this.staffUserId, + this.primaryWeek1EndDate, + this.primaryWeek2EndDate, + this.primaryWeek3EndDate, + this.rosterEndDate, + this.shiftStartTime, + this.shiftEndTime, + this.workHrs, + this.patientId, + this.secondaryWeeks, + this.primaryWeekNo, + this.noOfShifts, + this.addedby, + this.active, + this.isDelete, + this.days, + this.id, + this.templateId,}); + + ShiftArray.fromJson(dynamic json) { + liveRoster = json['liveRoster'] != null ? LiveRoster.fromJson(json['liveRoster']) : null; + staffHolidays = json['staffHolidays'] != null ? StaffHolidays.fromJson(json['staffHolidays']) : null; + staffUserId = json['staffUserId'] != null ? StaffMembers.fromJson(json['staffUserId']) : null; + primaryWeek1EndDate = json['primaryWeek1EndDate']; + primaryWeek2EndDate = json['primaryWeek2EndDate']; + primaryWeek3EndDate = json['primaryWeek3EndDate']; + rosterEndDate = json['rosterEndDate']; + shiftStartTime = json['shiftStartTime']; + shiftEndTime = json['shiftEndTime']; + workHrs = json['workHrs']; + patientId = json['patientId'] != null ? UserData.fromJson(json['patientId']) : null; + secondaryWeeks = json['secondaryWeeks'] != null ? json['secondaryWeeks'].cast() : []; + primaryWeekNo = json['primaryWeekNo']; + noOfShifts = json['noOfShifts']; + addedby = json['addedby']; + active = json['active']; + isDelete = json['isDelete']; + if (json['days'] != null) { + days = []; + json['days'].forEach((v) { + days?.add(Days.fromJson(v)); + }); + } + id = json['_id']; + templateId = json['templateId']; + } + LiveRoster? liveRoster; + StaffHolidays? staffHolidays; + StaffMembers? staffUserId; + int? primaryWeek1EndDate; + int? primaryWeek2EndDate; + int? primaryWeek3EndDate; + int? rosterEndDate; + int? shiftStartTime; + int? shiftEndTime; + int? workHrs; + UserData? patientId; + List? secondaryWeeks; + int? primaryWeekNo; + int? noOfShifts; + String? addedby; + bool? active; + bool? isDelete; + List? days; + String? id; + String? templateId; + + Map toJson() { + final map = {}; + if (liveRoster != null) { + map['liveRoster'] = liveRoster?.toJson(); + } + if (staffHolidays != null) { + map['staffHolidays'] = staffHolidays?.toJson(); + } + if (staffUserId != null) { + map['staffUserId'] = staffUserId?.toJson(); + } + map['primaryWeek1EndDate'] = primaryWeek1EndDate; + map['primaryWeek2EndDate'] = primaryWeek2EndDate; + map['primaryWeek3EndDate'] = primaryWeek3EndDate; + map['rosterEndDate'] = rosterEndDate; + map['shiftStartTime'] = shiftStartTime; + map['shiftEndTime'] = shiftEndTime; + map['workHrs'] = workHrs; + if (patientId != null) { + map['patientId'] = patientId?.toJson(); + } + map['secondaryWeeks'] = secondaryWeeks; + map['primaryWeekNo'] = primaryWeekNo; + map['noOfShifts'] = noOfShifts; + map['addedby'] = addedby; + map['active'] = active; + map['isDelete'] = isDelete; + if (days != null) { + map['days'] = days?.map((v) => v.toJson()).toList(); + } + map['_id'] = id; + map['templateId'] = templateId; + return map; + } + +} \ No newline at end of file diff --git a/lib/models/rota/ShiftLocation.dart b/lib/models/rota/ShiftLocation.dart new file mode 100644 index 0000000..b92f3e0 --- /dev/null +++ b/lib/models/rota/ShiftLocation.dart @@ -0,0 +1,33 @@ +import 'WeekArrayData.dart'; + +class ShiftLocation { + ShiftLocation({ + this.locationId, + this.weekArray, + this.id,}); + + ShiftLocation.fromJson(dynamic json) { + locationId = json['locationId']; + if (json['weekArray'] != null && json['weekArray'] is List) { + weekArray = []; + json['weekArray'].forEach((v) { + weekArray?.add(WeekArrayData.fromJson(v)); + }); + } + id = json['_id']; + } + String? locationId; + List? weekArray; + String? id; + + Map toJson() { + final map = {}; + map['locationId'] = locationId; + if (weekArray != null) { + map['weekArray'] = weekArray?.map((v) => v.toJson()).toList(); + } + map['_id'] = id; + return map; + } + +} \ No newline at end of file diff --git a/lib/models/rota/ShiftLocationData.dart b/lib/models/rota/ShiftLocationData.dart new file mode 100644 index 0000000..274e974 --- /dev/null +++ b/lib/models/rota/ShiftLocationData.dart @@ -0,0 +1,36 @@ +class ShiftLocationData { + ShiftLocationData({ + this.id, + this.shiftLocationName, + this.addedby, + this.active, + this.createdAt, + this.updatedAt,}); + + ShiftLocationData.fromJson(dynamic json) { + id = json['_id']; + shiftLocationName = json['shiftLocationName']; + addedby = json['addedby']; + active = json['active']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + } + String? id; + String? shiftLocationName; + String? addedby; + bool? active; + String? createdAt; + String? updatedAt; + + Map toJson() { + final map = {}; + map['_id'] = id; + map['shiftLocationName'] = shiftLocationName; + map['addedby'] = addedby; + map['active'] = active; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + return map; + } + +} \ No newline at end of file diff --git a/lib/models/rota/StaffHolidays.dart b/lib/models/rota/StaffHolidays.dart new file mode 100644 index 0000000..d393ea0 --- /dev/null +++ b/lib/models/rota/StaffHolidays.dart @@ -0,0 +1,27 @@ +class StaffHolidays { + StaffHolidays({ + this.isStaffHolidays, + this.calculatedDates,}); + + StaffHolidays.fromJson(dynamic json) { + isStaffHolidays = json['isStaffHolidays']; + // if (json['calculatedDates'] != null) { + // calculatedDates = []; + // json['calculatedDates'].forEach((v) { + // calculatedDates?.add(Dynamic.fromJson(v)); + // }); + // } + } + bool? isStaffHolidays; + List? calculatedDates; + + Map toJson() { + final map = {}; + map['isStaffHolidays'] = isStaffHolidays; + if (calculatedDates != null) { + map['calculatedDates'] = calculatedDates?.map((v) => v.toJson()).toList(); + } + return map; + } + +} \ No newline at end of file diff --git a/lib/models/rota/WeekArrayData.dart b/lib/models/rota/WeekArrayData.dart new file mode 100644 index 0000000..2180e20 --- /dev/null +++ b/lib/models/rota/WeekArrayData.dart @@ -0,0 +1,838 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/models/profile_screen_model.dart'; +import 'package:ftc_mobile_app/utilities/date_formatter.dart'; + +import 'ShiftLocationData.dart'; + +class WeekArrayData { + WeekArrayData({ + this.weekNo, + this.daysArray, + this.id, + }); + + WeekArrayData.fromJson(dynamic json) { + weekNo = json['weekNo']; + if (json['daysArray'] != null) { + daysArray = []; + json['daysArray'].forEach((v) { + daysArray?.add(DaysArrayData.fromJson(v)); + }); + } + id = json['_id']; + } + + int? weekNo; + List? daysArray; + String? id; + + Map toJson() { + final map = {}; + map['weekNo'] = weekNo; + if (daysArray != null) { + map['daysArray'] = daysArray?.map((v) => v.toJson()).toList(); + } + map['_id'] = id; + return map; + } +} + +class DaysArrayData { + DaysArrayData({ + this.dayName, + this.staffAssigned, + this.serviceUserAssigned, + this.staffUserId, + this.shiftDate, + this.shiftStartTime, + this.shiftEndTime, + this.workHrs, + this.note, + this.serviceUserId, + this.isSleepOver, + this.isOverNightStay, + this.lastModifiedBy, + this.active, + this.isDelete, + this.locationId, + this.rosterId, + this.isRequested, + this.id, + }); + + DaysArrayData.fromJson(dynamic json) { + dayName = json['dayName']; + staffAssigned = json['staffAssigned']; + serviceUserAssigned = json['serviceUserAssigned']; + staffUserId = json['staffUserId'] != null + ? StaffMembers.fromJson(json['staffUserId']) + : null; + shiftDate = json['shiftDate']; + shiftStartTime = json['shiftStartTime']; + shiftEndTime = json['shiftEndTime']; + workHrs = json['workHrs']; + note = json['note']; + serviceUserId = json['serviceUserId'] != null + ? UserData.fromJson(json['serviceUserId']) + : null; + locationId = json['locationId'] is Map + ? ShiftLocationData.fromJson(json['locationId']) + : null; + isSleepOver = json['isSleepOver']; + isOverNightStay = json['isOverNightStay']; + lastModifiedBy = json['lastModifiedBy']; + active = json['active']; + isDelete = json['isDelete']; + rosterId = json['rosterId']; + isRequested = json['isRequested']; + id = json['_id']; + + if (shiftDate != null) { + shiftDateTime = DateTime.fromMillisecondsSinceEpoch(shiftDate!).toLocal(); + } + + final f = DateFormatter(); + startTime = f.time24to12format(time: shiftStartTime ?? ""); + endTime = f.time24to12format(time: shiftEndTime ?? ""); + } + + String? dayName; + bool? staffAssigned; + bool? serviceUserAssigned; + StaffMembers? staffUserId; + int? shiftDate; + String? shiftStartTime; + String? shiftEndTime; + int? workHrs; + String? note; + UserData? serviceUserId; + bool? isSleepOver; + bool? isOverNightStay; + String? lastModifiedBy; + bool? active; + bool? isDelete; + String? rosterId; + bool? isRequested; + String? id; + ShiftLocationData? locationId; + + // local usage vars + DateTime? shiftDateTime; + + ///stores 12hour format of [shiftStartTime] and [shiftEndTime] + TimeOfDay? startTime, endTime; + + Map toJson() { + final map = {}; + map['dayName'] = dayName; + map['staffAssigned'] = staffAssigned; + map['serviceUserAssigned'] = serviceUserAssigned; + if (staffUserId != null) { + map['staffUserId'] = staffUserId?.toJson(); + } + map['shiftDate'] = shiftDate; + map['shiftStartTime'] = shiftStartTime; + map['shiftEndTime'] = shiftEndTime; + map['workHrs'] = workHrs; + map['note'] = note; + if (serviceUserId != null) { + map['serviceUserId'] = serviceUserId?.toJson(); + } + if (locationId != null) { + map['locationId'] = locationId?.toJson(); + } + map['isSleepOver'] = isSleepOver; + map['isOverNightStay'] = isOverNightStay; + map['lastModifiedBy'] = lastModifiedBy; + map['active'] = active; + map['isDelete'] = isDelete; + map['rosterId'] = rosterId; + map['isRequested'] = isRequested; + map['_id'] = id; + return map; + } +} + +// class ServiceUserId { +// ServiceUserId({ +// this.fcmTokens, +// this.location, +// this.profileVideoUrl, +// this.id, +// this.userModelName, +// this.name, +// this.version, +// this.email, +// this.phoneNumber, +// this.active, +// this.role, +// this.profilePictureUrl, +// this.deviceId, +// this.verificationCode, +// this.isVerified, +// this.approved, +// this.blocked, +// this.createdAt, +// this.updatedAt, +// this.v, +// this.password, +// this.userSettings, +// this.modelId,}); +// +// ServiceUserId.fromJson(dynamic json) { +// fcmTokens = json['fcm_tokens'] != null ? FcmTokens.fromJson(json['fcm_tokens']) : null; +// location = json['location'] != null ? Location.fromJson(json['location']) : null; +// profileVideoUrl = json['profile_video_url']; +// id = json['_id']; +// userModelName = json['userModelName']; +// name = json['name']; +// version = json['version']; +// email = json['email']; +// phoneNumber = json['phoneNumber']; +// active = json['active']; +// role = json['role']; +// profilePictureUrl = json['profile_picture_url']; +// deviceId = json['deviceId']; +// verificationCode = json['verification_code']; +// isVerified = json['is_verified']; +// approved = json['approved']; +// blocked = json['blocked']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// password = json['password']; +// userSettings = json['userSettings']; +// modelId = json['modelId'] != null ? ModelId.fromJson(json['modelId']) : null; +// } +// FcmTokens? fcmTokens; +// Location? location; +// String? profileVideoUrl; +// String? id; +// String? userModelName; +// String? name; +// String? version; +// String? email; +// String? phoneNumber; +// bool? active; +// String? role; +// String? profilePictureUrl; +// String? deviceId; +// String? verificationCode; +// bool? isVerified; +// bool? approved; +// bool? blocked; +// String? createdAt; +// String? updatedAt; +// int? v; +// String? password; +// String? userSettings; +// ModelId? modelId; +// +// Map toJson() { +// final map = {}; +// if (fcmTokens != null) { +// map['fcm_tokens'] = fcmTokens?.toJson(); +// } +// if (location != null) { +// map['location'] = location?.toJson(); +// } +// map['profile_video_url'] = profileVideoUrl; +// map['_id'] = id; +// map['userModelName'] = userModelName; +// map['name'] = name; +// map['version'] = version; +// map['email'] = email; +// map['phoneNumber'] = phoneNumber; +// map['active'] = active; +// map['role'] = role; +// map['profile_picture_url'] = profilePictureUrl; +// map['deviceId'] = deviceId; +// map['verification_code'] = verificationCode; +// map['is_verified'] = isVerified; +// map['approved'] = approved; +// map['blocked'] = blocked; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// map['password'] = password; +// map['userSettings'] = userSettings; +// if (modelId != null) { +// map['modelId'] = modelId?.toJson(); +// } +// return map; +// } +// +// } +// +// class ModelId { +// ModelId({ +// this.aboutPatient, +// this.id, +// this.suLastName, +// this.suFirstMiddleName, +// this.suPreferredName, +// this.name, +// this.suSsn, +// this.providerName, +// this.suSex, +// this.suTitle, +// this.suDOB, +// this.suAge, +// this.suReferredBY, +// this.suFamilyHead, +// this.suAddress1, +// this.suAddress2, +// this.suCity, +// this.suState, +// this.suZip, +// this.suFirstVisitDate, +// this.suLastVisitDate, +// this.suProvider, +// this.currSU, +// this.suHomePhone, +// this.suWorkPhone, +// this.suMobileHomeNo, +// this.suMobileWorkNo, +// this.suEmailHome, +// this.suEmailWork, +// this.suPrefHomeNo, +// this.suPrefWorkNo, +// this.suEmergencyContact, +// this.seMedicalAlert, +// this.suNote, +// this.diagnosises, +// this.user, +// this.shifts, +// this.serviceUserMedications, +// this.homeVisitSignOut, +// this.srUsShiftsRequired, +// this.suEnquiries, +// this.active, +// this.createdAt, +// this.updatedAt, +// this.v,}); +// +// ModelId.fromJson(dynamic json) { +// aboutPatient = json['aboutPatient'] != null ? AboutPatient.fromJson(json['aboutPatient']) : null; +// id = json['_id']; +// suLastName = json['suLastName']; +// suFirstMiddleName = json['suFirstMiddleName']; +// suPreferredName = json['suPreferredName']; +// name = json['name']; +// suSsn = json['suSsn']; +// providerName = json['providerName']; +// suSex = json['suSex']; +// suTitle = json['suTitle']; +// suDOB = json['suDOB']; +// suAge = json['suAge']; +// suReferredBY = json['suReferredBY']; +// suFamilyHead = json['suFamilyHead']; +// suAddress1 = json['suAddress1']; +// suAddress2 = json['suAddress2']; +// suCity = json['suCity']; +// suState = json['suState']; +// suZip = json['suZip']; +// suFirstVisitDate = json['suFirstVisitDate']; +// suLastVisitDate = json['suLastVisitDate']; +// if (json['suProvider'] != null) { +// suProvider = []; +// json['suProvider'].forEach((v) { +// suProvider?.add(Dynamic.fromJson(v)); +// }); +// } +// currSU = json['currSU']; +// suHomePhone = json['suHomePhone']; +// suWorkPhone = json['suWorkPhone']; +// suMobileHomeNo = json['suMobileHomeNo']; +// suMobileWorkNo = json['suMobileWorkNo']; +// suEmailHome = json['suEmailHome']; +// suEmailWork = json['suEmailWork']; +// suPrefHomeNo = json['suPrefHomeNo']; +// suPrefWorkNo = json['suPrefWorkNo']; +// suEmergencyContact = json['suEmergencyContact']; +// seMedicalAlert = json['seMedicalAlert']; +// suNote = json['suNote']; +// if (json['diagnosises'] != null) { +// diagnosises = []; +// json['diagnosises'].forEach((v) { +// diagnosises?.add(Diagnosises.fromJson(v)); +// }); +// } +// user = json['user']; +// if (json['shifts'] != null) { +// shifts = []; +// json['shifts'].forEach((v) { +// shifts?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['serviceUserMedications'] != null) { +// serviceUserMedications = []; +// json['serviceUserMedications'].forEach((v) { +// serviceUserMedications?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['homeVisitSignOut'] != null) { +// homeVisitSignOut = []; +// json['homeVisitSignOut'].forEach((v) { +// homeVisitSignOut?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['srUsShiftsRequired'] != null) { +// srUsShiftsRequired = []; +// json['srUsShiftsRequired'].forEach((v) { +// srUsShiftsRequired?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['suEnquiries'] != null) { +// suEnquiries = []; +// json['suEnquiries'].forEach((v) { +// suEnquiries?.add(Dynamic.fromJson(v)); +// }); +// } +// active = json['active']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// } +// AboutPatient? aboutPatient; +// String? id; +// String? suLastName; +// String? suFirstMiddleName; +// String? suPreferredName; +// String? name; +// String? suSsn; +// String? providerName; +// String? suSex; +// String? suTitle; +// String? suDOB; +// String? suAge; +// String? suReferredBY; +// String? suFamilyHead; +// String? suAddress1; +// String? suAddress2; +// String? suCity; +// String? suState; +// String? suZip; +// String? suFirstVisitDate; +// String? suLastVisitDate; +// List? suProvider; +// bool? currSU; +// String? suHomePhone; +// String? suWorkPhone; +// String? suMobileHomeNo; +// String? suMobileWorkNo; +// String? suEmailHome; +// String? suEmailWork; +// String? suPrefHomeNo; +// String? suPrefWorkNo; +// String? suEmergencyContact; +// String? seMedicalAlert; +// String? suNote; +// List? diagnosises; +// String? user; +// List? shifts; +// List? serviceUserMedications; +// List? homeVisitSignOut; +// List? srUsShiftsRequired; +// List? suEnquiries; +// bool? active; +// String? createdAt; +// String? updatedAt; +// int? v; +// +// Map toJson() { +// final map = {}; +// if (aboutPatient != null) { +// map['aboutPatient'] = aboutPatient?.toJson(); +// } +// map['_id'] = id; +// map['suLastName'] = suLastName; +// map['suFirstMiddleName'] = suFirstMiddleName; +// map['suPreferredName'] = suPreferredName; +// map['name'] = name; +// map['suSsn'] = suSsn; +// map['providerName'] = providerName; +// map['suSex'] = suSex; +// map['suTitle'] = suTitle; +// map['suDOB'] = suDOB; +// map['suAge'] = suAge; +// map['suReferredBY'] = suReferredBY; +// map['suFamilyHead'] = suFamilyHead; +// map['suAddress1'] = suAddress1; +// map['suAddress2'] = suAddress2; +// map['suCity'] = suCity; +// map['suState'] = suState; +// map['suZip'] = suZip; +// map['suFirstVisitDate'] = suFirstVisitDate; +// map['suLastVisitDate'] = suLastVisitDate; +// if (suProvider != null) { +// map['suProvider'] = suProvider?.map((v) => v.toJson()).toList(); +// } +// map['currSU'] = currSU; +// map['suHomePhone'] = suHomePhone; +// map['suWorkPhone'] = suWorkPhone; +// map['suMobileHomeNo'] = suMobileHomeNo; +// map['suMobileWorkNo'] = suMobileWorkNo; +// map['suEmailHome'] = suEmailHome; +// map['suEmailWork'] = suEmailWork; +// map['suPrefHomeNo'] = suPrefHomeNo; +// map['suPrefWorkNo'] = suPrefWorkNo; +// map['suEmergencyContact'] = suEmergencyContact; +// map['seMedicalAlert'] = seMedicalAlert; +// map['suNote'] = suNote; +// if (diagnosises != null) { +// map['diagnosises'] = diagnosises?.map((v) => v.toJson()).toList(); +// } +// map['user'] = user; +// if (shifts != null) { +// map['shifts'] = shifts?.map((v) => v.toJson()).toList(); +// } +// if (serviceUserMedications != null) { +// map['serviceUserMedications'] = serviceUserMedications?.map((v) => v.toJson()).toList(); +// } +// if (homeVisitSignOut != null) { +// map['homeVisitSignOut'] = homeVisitSignOut?.map((v) => v.toJson()).toList(); +// } +// if (srUsShiftsRequired != null) { +// map['srUsShiftsRequired'] = srUsShiftsRequired?.map((v) => v.toJson()).toList(); +// } +// if (suEnquiries != null) { +// map['suEnquiries'] = suEnquiries?.map((v) => v.toJson()).toList(); +// } +// map['active'] = active; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// return map; +// } +// +// } +// +// class Diagnosises { +// Diagnosises({ +// this.diagnosisText, +// this.diagnosisDate, +// this.diagnosisBy, +// this.isCurrentDiagnosis, +// this.id,}); +// +// Diagnosises.fromJson(dynamic json) { +// diagnosisText = json['diagnosisText']; +// diagnosisDate = json['diagnosisDate']; +// diagnosisBy = json['diagnosisBy']; +// isCurrentDiagnosis = json['isCurrentDiagnosis']; +// id = json['_id']; +// } +// String? diagnosisText; +// String? diagnosisDate; +// String? diagnosisBy; +// bool? isCurrentDiagnosis; +// String? id; +// +// Map toJson() { +// final map = {}; +// map['diagnosisText'] = diagnosisText; +// map['diagnosisDate'] = diagnosisDate; +// map['diagnosisBy'] = diagnosisBy; +// map['isCurrentDiagnosis'] = isCurrentDiagnosis; +// map['_id'] = id; +// return map; +// } +// +// } +// +// class AboutPatient { +// AboutPatient({ +// this.aboutText, +// this.aboutDate, +// this.aboutBy,}); +// +// AboutPatient.fromJson(dynamic json) { +// aboutText = json['aboutText']; +// aboutDate = json['aboutDate']; +// aboutBy = json['aboutBy']; +// } +// String? aboutText; +// String? aboutDate; +// String? aboutBy; +// +// Map toJson() { +// final map = {}; +// map['aboutText'] = aboutText; +// map['aboutDate'] = aboutDate; +// map['aboutBy'] = aboutBy; +// return map; +// } +// +// } +// +// class Location { +// Location({ +// this.type, +// this.coordinates,}); +// +// Location.fromJson(dynamic json) { +// type = json['type']; +// coordinates = json['coordinates'] != null ? json['coordinates'].cast() : []; +// } +// String? type; +// List? coordinates; +// +// Map toJson() { +// final map = {}; +// map['type'] = type; +// map['coordinates'] = coordinates; +// return map; +// } +// +// } +// +// class FcmTokens { +// FcmTokens({ +// this.token, +// this.deviceType,}); +// +// FcmTokens.fromJson(dynamic json) { +// token = json['token']; +// deviceType = json['deviceType']; +// } +// String? token; +// String? deviceType; +// +// Map toJson() { +// final map = {}; +// map['token'] = token; +// map['deviceType'] = deviceType; +// return map; +// } +// +// } + +// class StaffUserId { +// StaffUserId({ +// this.contractedHours, +// this.id, +// this.staffMemberName, +// this.staffDesignation, +// this.staffOnLeave, +// this.supervisorId, +// this.holidays, +// this.complianceDocuments, +// this.niNumber, +// this.kin, +// this.user, +// this.clients, +// this.staffWorkLoads, +// this.staffHolidayRequests, +// this.staffTrainings, +// this.supervision, +// this.underSupervisions, +// this.staffWorkingDays, +// this.stafDob, +// this.active, +// this.covidCheck, +// this.createdAt, +// this.updatedAt, +// this.v, +// this.staffDisciplinaries, +// this.staffReferences,}); +// +// StaffUserId.fromJson(dynamic json) { +// contractedHours = json['contractedHours'] != null ? ContractedHours.fromJson(json['contractedHours']) : null; +// id = json['_id']; +// staffMemberName = json['staffMemberName']; +// staffDesignation = json['staffDesignation']; +// staffOnLeave = json['staffOnLeave']; +// supervisorId = json['supervisorId']; +// if (json['holidays'] != null) { +// holidays = []; +// json['holidays'].forEach((v) { +// holidays?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['complianceDocuments'] != null) { +// complianceDocuments = []; +// json['complianceDocuments'].forEach((v) { +// complianceDocuments?.add(Dynamic.fromJson(v)); +// }); +// } +// niNumber = json['niNumber']; +// kin = json['kin']; +// user = json['user']; +// if (json['clients'] != null) { +// clients = []; +// json['clients'].forEach((v) { +// clients?.add(Dynamic.fromJson(v)); +// }); +// } +// staffWorkLoads = json['staffWorkLoads'] != null ? json['staffWorkLoads'].cast() : []; +// if (json['staffHolidayRequests'] != null) { +// staffHolidayRequests = []; +// json['staffHolidayRequests'].forEach((v) { +// staffHolidayRequests?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['staffTrainings'] != null) { +// staffTrainings = []; +// json['staffTrainings'].forEach((v) { +// staffTrainings?.add(Dynamic.fromJson(v)); +// }); +// } +// supervision = json['supervision'] != null ? Supervision.fromJson(json['supervision']) : null; +// if (json['underSupervisions'] != null) { +// underSupervisions = []; +// json['underSupervisions'].forEach((v) { +// underSupervisions?.add(Dynamic.fromJson(v)); +// }); +// } +// if (json['staffWorkingDays'] != null) { +// staffWorkingDays = []; +// json['staffWorkingDays'].forEach((v) { +// staffWorkingDays?.add(Dynamic.fromJson(v)); +// }); +// } +// stafDob = json['stafDob']; +// active = json['active']; +// covidCheck = json['covidCheck']; +// createdAt = json['createdAt']; +// updatedAt = json['updatedAt']; +// v = json['__v']; +// staffDisciplinaries = json['staffDisciplinaries']; +// staffReferences = json['staffReferences']; +// } +// ContractedHours? contractedHours; +// String? id; +// String? staffMemberName; +// String? staffDesignation; +// bool? staffOnLeave; +// String? supervisorId; +// List? holidays; +// List? complianceDocuments; +// String? niNumber; +// String? kin; +// String? user; +// List? clients; +// List? staffWorkLoads; +// List? staffHolidayRequests; +// List? staffTrainings; +// Supervision? supervision; +// List? underSupervisions; +// List? staffWorkingDays; +// String? stafDob; +// bool? active; +// bool? covidCheck; +// String? createdAt; +// String? updatedAt; +// int? v; +// String? staffDisciplinaries; +// String? staffReferences; +// +// Map toJson() { +// final map = {}; +// if (contractedHours != null) { +// map['contractedHours'] = contractedHours?.toJson(); +// } +// map['_id'] = id; +// map['staffMemberName'] = staffMemberName; +// map['staffDesignation'] = staffDesignation; +// map['staffOnLeave'] = staffOnLeave; +// map['supervisorId'] = supervisorId; +// if (holidays != null) { +// map['holidays'] = holidays?.map((v) => v.toJson()).toList(); +// } +// if (complianceDocuments != null) { +// map['complianceDocuments'] = complianceDocuments?.map((v) => v.toJson()).toList(); +// } +// map['niNumber'] = niNumber; +// map['kin'] = kin; +// map['user'] = user; +// if (clients != null) { +// map['clients'] = clients?.map((v) => v.toJson()).toList(); +// } +// map['staffWorkLoads'] = staffWorkLoads; +// if (staffHolidayRequests != null) { +// map['staffHolidayRequests'] = staffHolidayRequests?.map((v) => v.toJson()).toList(); +// } +// if (staffTrainings != null) { +// map['staffTrainings'] = staffTrainings?.map((v) => v.toJson()).toList(); +// } +// if (supervision != null) { +// map['supervision'] = supervision?.toJson(); +// } +// if (underSupervisions != null) { +// map['underSupervisions'] = underSupervisions?.map((v) => v.toJson()).toList(); +// } +// if (staffWorkingDays != null) { +// map['staffWorkingDays'] = staffWorkingDays?.map((v) => v.toJson()).toList(); +// } +// map['stafDob'] = stafDob; +// map['active'] = active; +// map['covidCheck'] = covidCheck; +// map['createdAt'] = createdAt; +// map['updatedAt'] = updatedAt; +// map['__v'] = v; +// map['staffDisciplinaries'] = staffDisciplinaries; +// map['staffReferences'] = staffReferences; +// return map; +// } +// +// } +// +// class Supervision { +// Supervision({ +// this.supervisionName, +// this.sprDueDate, +// this.sprStatus, +// this.sprResult, +// this.templateTitleId,}); +// +// Supervision.fromJson(dynamic json) { +// supervisionName = json['supervisionName']; +// sprDueDate = json['sprDueDate']; +// sprStatus = json['sprStatus']; +// sprResult = json['sprResult']; +// templateTitleId = json['templateTitleId']; +// } +// String? supervisionName; +// String? sprDueDate; +// String? sprStatus; +// String? sprResult; +// String? templateTitleId; +// +// Map toJson() { +// final map = {}; +// map['supervisionName'] = supervisionName; +// map['sprDueDate'] = sprDueDate; +// map['sprStatus'] = sprStatus; +// map['sprResult'] = sprResult; +// map['templateTitleId'] = templateTitleId; +// return map; +// } +// +// } +// +// class ContractedHours { +// ContractedHours({ +// this.contractedHours, +// this.totalShiftHoursWeek, +// this.noOfShifts,}); +// +// ContractedHours.fromJson(dynamic json) { +// contractedHours = json['contractedHours']; +// totalShiftHoursWeek = json['totalShiftHoursWeek']; +// noOfShifts = json['noOfShifts']; +// } +// int? contractedHours; +// int? totalShiftHoursWeek; +// int? noOfShifts; +// +// Map toJson() { +// final map = {}; +// map['contractedHours'] = contractedHours; +// map['totalShiftHoursWeek'] = totalShiftHoursWeek; +// map['noOfShifts'] = noOfShifts; +// return map; +// } +// +// } diff --git a/lib/models/rota_shift_model.dart b/lib/models/rota_shift_model.dart new file mode 100644 index 0000000..5726a74 --- /dev/null +++ b/lib/models/rota_shift_model.dart @@ -0,0 +1,173 @@ +class RotaShift { + String managerName = ''; + String name = ''; + String staffRequired = ''; + String workerType = ''; + String location = ''; + String id = ''; //from service + String startTime = ''; //from service + String endTime = ''; //from service + String breakTime = ''; + String notes = ''; + + RotaShift.empty(); + + bool get havingShift { + return id.isNotEmpty || startTime.isNotEmpty || endTime.isNotEmpty; + } + + DateTime get shiftTime { + return DateTime.parse(startTime).toLocal(); + } + + DateTime get shiftStartTime { + return DateTime.parse(startTime).toLocal(); + } + + DateTime get shiftEndTime { + return endTime.isNotEmpty + ? DateTime.parse(endTime).toLocal() + : DateTime.now(); + } + + RotaShift({ + this.id = '', + required this.name, + required this.staffRequired, + required this.workerType, + required this.location, + required this.startTime, + required this.endTime, + required this.breakTime, + required this.notes, + this.managerName = "Emily Smith", + }); + + RotaShift.fromJson(Map json) { + id = json["_id"] ?? ""; + startTime = json["shftStartTime"] ?? ""; + endTime = json["shftEndTime"] ?? ""; + } + + @override + String toString() { + return 'RotaShift{managerName: $managerName, name: $name, staffRequired: $staffRequired, workerType: $workerType, location: $location, id: $id, startTime: $startTime, endTime: $endTime, breakTime: $breakTime, notes: $notes}'; + } +} + +class DayWiseRecord { + bool dayOn = false; + bool sleepOverRequired = false; + RotaShift rotaShift = RotaShift.empty(); + + DayWiseRecord.empty(); + + DayWiseRecord({ + required this.dayOn, + required this.sleepOverRequired, + required this.rotaShift, + }); + + DayWiseRecord.fromJson(Map json) { + dayOn = json["dayOn"] ?? false; + sleepOverRequired = json["sleepOverRequired"] ?? false; + if (json["shifts"] is List && json['shifts'].length != 0) { + rotaShift = RotaShift.fromJson(json["shifts"][0] ?? ""); + } + } + + @override + String toString() { + return 'DayWiseRecord{dayOn: $dayOn, sleepOverRequired: $sleepOverRequired, rotaShift: $rotaShift}'; + } +} + +class WeekWiseRecord { + DayWiseRecord mondayRecord = DayWiseRecord.empty(); + DayWiseRecord tuesdayRecord = DayWiseRecord.empty(); + DayWiseRecord wednesdayRecord = DayWiseRecord.empty(); + DayWiseRecord thursdayRecord = DayWiseRecord.empty(); + DayWiseRecord fridayRecord = DayWiseRecord.empty(); + DayWiseRecord saturdayRecord = DayWiseRecord.empty(); + DayWiseRecord sundayRecord = DayWiseRecord.empty(); + bool weekIncluded = false; + + WeekWiseRecord.empty(); + + WeekWiseRecord({ + required this.mondayRecord, + required this.tuesdayRecord, + required this.wednesdayRecord, + required this.thursdayRecord, + required this.fridayRecord, + required this.saturdayRecord, + required this.sundayRecord, + required this.weekIncluded, + }); + + WeekWiseRecord.fromJson(dynamic json) { + mondayRecord = DayWiseRecord.fromJson(json['monday'] ?? ""); + tuesdayRecord = DayWiseRecord.fromJson(json['tuesday'] ?? ""); + wednesdayRecord = DayWiseRecord.fromJson(json['wednesday'] ?? ""); + thursdayRecord = DayWiseRecord.fromJson(json['thursday'] ?? ""); + fridayRecord = DayWiseRecord.fromJson(json['friday'] ?? ""); + saturdayRecord = DayWiseRecord.fromJson(json['saturday'] ?? ""); + sundayRecord = DayWiseRecord.fromJson(json['sunday'] ?? ""); + weekIncluded = json['weekIncluded'] ?? false; + } + + @override + String toString() { + return 'WeekWiseRecord{mondayRecord: $mondayRecord, tuesdayRecord: $tuesdayRecord, wednesdayRecord: $wednesdayRecord, thursdayRecord: $thursdayRecord, fridayRecord: $fridayRecord, saturdayRecord: $saturdayRecord, sundayRecord: $sundayRecord, weekIncluded: $weekIncluded}'; + } +} + +//make variable for this model +class MonthWiseRecord { + WeekWiseRecord week1 = WeekWiseRecord.empty(); + WeekWiseRecord week2 = WeekWiseRecord.empty(); + WeekWiseRecord week3 = WeekWiseRecord.empty(); + WeekWiseRecord week4 = WeekWiseRecord.empty(); + String id = ""; + String serviceUser = ""; + String addedBy = ""; + String createdAt = ""; + String updatedAt = ""; + bool active = false; + int v = -1; + + MonthWiseRecord.empty(); + + MonthWiseRecord({ + required this.week1, + required this.week2, + required this.week3, + required this.week4, + required this.id, + required this.serviceUser, + required this.addedBy, + required this.createdAt, + required this.updatedAt, + required this.active, + required this.v, + }); + + MonthWiseRecord.fromJson(Map json) { + week1 = WeekWiseRecord.fromJson(json['week1'] ?? {}); + week2 = WeekWiseRecord.fromJson(json['week2'] ?? {}); + week3 = WeekWiseRecord.fromJson(json['week3'] ?? {}); + week4 = WeekWiseRecord.fromJson(json['week4'] ?? {}); + id = json["id"] ?? ""; + serviceUser = json["serviceUser"] ?? ""; + addedBy = json["addedBy"] ?? ""; + createdAt = json["createdAt"] ?? ""; + updatedAt = json["updatedAt"] ?? ""; + active = json["active"] ?? false; + v = json["v"] ?? -1; + } + + @override + String toString() { + return 'MonthWiseRecord{week1: $week1, week2: $week2, week3: $week3, week4: $week4, id: $id, serviceUser: $serviceUser, addedBy: $addedBy, createdAt: $createdAt, updatedAt: $updatedAt, active: $active, v: $v}'; + } +} diff --git a/lib/models/staffWorkload/StaffWorkloadResponse.dart b/lib/models/staffWorkload/StaffWorkloadResponse.dart new file mode 100644 index 0000000..77d9618 --- /dev/null +++ b/lib/models/staffWorkload/StaffWorkloadResponse.dart @@ -0,0 +1,208 @@ +class StaffWorkloadResponse { + StaffWorkloadResponse({ + this.status, + this.message, + this.data,}); + + StaffWorkloadResponse.fromJson(dynamic json) { + status = json['status']; + message = json['message']; + data = json['data'] != null ? StaffWorkloadData.fromJson(json['data']) : null; + } + String? status; + String? message; + StaffWorkloadData? data; + + Map toJson() { + final map = {}; + map['status'] = status; + map['message'] = message; + if (data != null) { + map['data'] = data?.toJson(); + } + return map; + } + +} + +class StaffWorkloadData { + StaffWorkloadData({ + this.staffWorkLoads, + this.count, + this.offset, + this.limit,}); + + StaffWorkloadData.fromJson(dynamic json) { + if (json['staffWorkLoads'] != null) { + staffWorkLoads = []; + json['staffWorkLoads'].forEach((v) { + staffWorkLoads?.add(StaffWorkLoads.fromJson(v)); + }); + } + count = json['count']; + offset = json['offset']; + limit = json['limit']; + } + List? staffWorkLoads; + int? count; + int? offset; + int? limit; + + Map toJson() { + final map = {}; + if (staffWorkLoads != null) { + map['staffWorkLoads'] = staffWorkLoads?.map((v) => v.toJson()).toList(); + } + map['count'] = count; + map['offset'] = offset; + map['limit'] = limit; + return map; + } + +} + +class StaffWorkLoads { + StaffWorkLoads({ + this.holidayEntitlement, + this.id, + this.isCurrentWrkLd, + this.startDate, + this.endDate, + this.holidayAlwnNoOfDys, + this.holidayAlwnNoOfHours, + this.holidaysAvailed, + this.holidaysRemaining, + this.staffMember, + this.carriedOverHours, + this.active, + this.createdAt, + this.updatedAt,}); + + StaffWorkLoads.fromJson(dynamic json) { + holidayEntitlement = json['holidayEntitlement'] != null ? HolidayEntitlement.fromJson(json['holidayEntitlement']) : null; + id = json['_id']; + isCurrentWrkLd = json['isCurrentWrkLd']; + startDate = json['startDate']; + endDate = json['endDate']; + holidayAlwnNoOfDys = json['holidayAlwnNoOfDys']; + holidayAlwnNoOfHours = json['holidayAlwnNoOfHours']; + holidaysAvailed = json['holidaysAvailed']; + holidaysRemaining = json['holidaysRemaining']; + staffMember = json['staffMember'] is Map ? StaffMember.fromJson(json['staffMember']) : null; + carriedOverHours = json['carriedOverHours']; + active = json['active']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + } + HolidayEntitlement? holidayEntitlement; + String? id; + bool? isCurrentWrkLd; + String? startDate; + String? endDate; + int? holidayAlwnNoOfDys; + int? holidayAlwnNoOfHours; + int? holidaysAvailed; + int? holidaysRemaining; + StaffMember? staffMember; + int? carriedOverHours; + bool? active; + String? createdAt; + String? updatedAt; + + Map toJson() { + final map = {}; + if (holidayEntitlement != null) { + map['holidayEntitlement'] = holidayEntitlement?.toJson(); + } + map['_id'] = id; + map['isCurrentWrkLd'] = isCurrentWrkLd; + map['startDate'] = startDate; + map['endDate'] = endDate; + map['holidayAlwnNoOfDys'] = holidayAlwnNoOfDys; + map['holidayAlwnNoOfHours'] = holidayAlwnNoOfHours; + map['holidaysAvailed'] = holidaysAvailed; + map['holidaysRemaining'] = holidaysRemaining; + if (staffMember != null) { + map['staffMember'] = staffMember?.toJson(); + } + map['carriedOverHours'] = carriedOverHours; + map['active'] = active; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + return map; + } + +} + +class StaffMember { + StaffMember({ + this.id, + this.user,}); + + StaffMember.fromJson(dynamic json) { + id = json['_id']; + user = json['user'] != null ? User.fromJson(json['user']) : null; + } + String? id; + User? user; + + Map toJson() { + final map = {}; + map['_id'] = id; + if (user != null) { + map['user'] = user?.toJson(); + } + return map; + } + +} + +class User { + User({ + this.id, + this.name, + this.profilePictureUrl,}); + + User.fromJson(dynamic json) { + id = json['_id']; + name = json['name']; + profilePictureUrl = json['profile_picture_url']; + } + String? id; + String? name; + String? profilePictureUrl; + + Map toJson() { + final map = {}; + map['_id'] = id; + map['name'] = name; + map['profile_picture_url'] = profilePictureUrl; + return map; + } + +} + +class HolidayEntitlement { + HolidayEntitlement({ + this.numberOfDays, + this.numberOfHours, + this.numberOfWeeks,}); + + HolidayEntitlement.fromJson(dynamic json) { + numberOfDays = json['numberOfDays']; + numberOfHours = json['numberOfHours']; + numberOfWeeks = json['numberOfWeeks']; + } + int? numberOfDays; + int? numberOfHours; + int? numberOfWeeks; + + Map toJson() { + final map = {}; + map['numberOfDays'] = numberOfDays; + map['numberOfHours'] = numberOfHours; + map['numberOfWeeks'] = numberOfWeeks; + return map; + } + +} \ No newline at end of file diff --git a/lib/models/training/TrainingResponseData.dart b/lib/models/training/TrainingResponseData.dart new file mode 100644 index 0000000..f651db2 --- /dev/null +++ b/lib/models/training/TrainingResponseData.dart @@ -0,0 +1,168 @@ +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; + +class TrainingResponseData { + TrainingResponseData({ + this.proposedTrainings, + this.count, + this.offset, + this.limit, + }); + + TrainingResponseData.fromJson(dynamic json) { + if (json['proposedTrainings'] != null) { + proposedTrainings = []; + json['proposedTrainings'].forEach((v) { + proposedTrainings?.add(TrainingUsers.fromJson(v)); + }); + } + count = json['count']; + offset = json['offset']; + limit = json['limit']; + } + + List? proposedTrainings; + int? count; + int? offset; + int? limit; + + Map toJson() { + final map = {}; + if (proposedTrainings != null) { + map['proposedTrainings'] = + proposedTrainings?.map((v) => v.toJson()).toList(); + } + map['count'] = count; + map['offset'] = offset; + map['limit'] = limit; + return map; + } +} + +class TrainingUsers { + TrainingUsers({ + this.id, + this.user, + this.trainingId, + this.active, + this.createdAt, + this.updatedAt, + }); + + TrainingUsers.fromJson(dynamic json) { + id = json['_id']; + user = json['user'] != null ? UserData.fromJson(json['user']) : null; + trainingId = json['trainingId'] != null + ? ProposedTrainings.fromJson(json['trainingId']) + : null; + active = json['active']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + } + + String? id; + UserData? user; + ProposedTrainings? trainingId; + bool? active; + String? createdAt; + String? updatedAt; + + Map toJson() { + final map = {}; + map['_id'] = id; + if (user != null) { + map['user'] = user?.toJson(); + } + if (trainingId != null) { + map['trainingId'] = trainingId?.toJson(); + } + map['active'] = active; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + return map; + } +} + +class ProposedTrainings { + ProposedTrainings({ + this.id, + this.prpsName, + this.prpsDescription, + this.prpsTrgType, + this.prpsTrgClass, + this.prpsTrgStatus, + this.prpsTrgStartDate, + this.prpsTrgEndDate, + this.prpsTrgRegisterationDate, + this.addedby, + this.active, + this.contentType, + this.content, + this.createdAt, + this.updatedAt, + // this.users, + }); + + ProposedTrainings.fromJson(dynamic json) { + id = json['_id']; + prpsName = json['prpsName']; + prpsDescription = json['prpsDescription']; + prpsTrgType = json['prpsTrgType']; + prpsTrgClass = json['prpsTrgClass']; + prpsTrgStatus = json['prpsTrgStatus']; + prpsTrgStartDate = json['prpsTrgStartDate']; + prpsTrgEndDate = json['prpsTrgEndDate']; + prpsTrgRegisterationDate = json['prpsTrgRegisterationDate']; + addedby = json['addedby']; + active = json['active']; + contentType = json['contentType']; + content = json['content'] != null ? json['content'].cast() : []; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + // if (json['users'] != null) { + // users = []; + // json['users'].forEach((v) { + // users?.add(TrainingUsers.fromJson(v)); + // }); + // } + } + + String? id; + String? prpsName; + String? prpsDescription; + String? prpsTrgType; + String? prpsTrgClass; + String? prpsTrgStatus; + int? prpsTrgStartDate; + int? prpsTrgEndDate; + int? prpsTrgRegisterationDate; + dynamic addedby; + bool? active; + String? contentType; + List? content; + String? createdAt; + String? updatedAt; + // List? users; + + Map toJson() { + final map = {}; + map['_id'] = id; + map['prpsName'] = prpsName; + map['prpsDescription'] = prpsDescription; + map['prpsTrgType'] = prpsTrgType; + map['prpsTrgClass'] = prpsTrgClass; + map['prpsTrgStatus'] = prpsTrgStatus; + map['prpsTrgStartDate'] = prpsTrgStartDate; + map['prpsTrgEndDate'] = prpsTrgEndDate; + map['prpsTrgRegisterationDate'] = prpsTrgRegisterationDate; + map['addedby'] = addedby; + map['active'] = active; + map['contentType'] = contentType; + map['content'] = content; + map['createdAt'] = createdAt; + map['updatedAt'] = updatedAt; + // if (users != null) { + // map['users'] = users?.map((v) => v.toJson()).toList(); + // } + return map; + } +} diff --git a/lib/models/user_model.dart b/lib/models/user_model.dart new file mode 100644 index 0000000..88233d3 --- /dev/null +++ b/lib/models/user_model.dart @@ -0,0 +1,224 @@ + +class UserModel { + FcmTokenModel fcmTokens = FcmTokenModel.empty(); + LocationModel locationModel = LocationModel.empty(); + String id = ""; + String userModelName = ""; + String name = ""; + String version = "" ; + String email = "" ; + String phoneNumber = ""; + bool active = false; + String role = "" ; + String profilePictureUrl = "" ; + String deviceId = "" ; + String verificationCode = "" ; + bool isVerified = false; + bool approved = false; + bool blocked = false; + String createdAt = "" ; + String updatedAt = "" ; + int v = -1; + String password = "" ; + UserSettingsModel userSettingsModel = UserSettingsModel.empty(); + bool newUser = false; + + //old fields for UI + String profilePicture = ""; + String homeAddress = ""; + String nextOfKin = ""; + String diagnosisHistory = ""; + String diagnosisDate = ""; + String aboutPatient = ""; + + UserModel.empty(); + + UserModel({ + required this.name, + required this.profilePicture, + required this.phoneNumber, + required this.homeAddress, + required this.nextOfKin, + required this.diagnosisHistory, + required this.diagnosisDate, + required this.aboutPatient, + this.role ="", + }); + + + @override + String toString() { + return 'UserModel{fcmTokens: $fcmTokens, locationModel: $locationModel, id: $id, userModelName: $userModelName, name: $name, version: $version, email: $email, phoneNumber: $phoneNumber, active: $active, role: $role, profilePictureUrl: $profilePictureUrl, deviceId: $deviceId, verificationCode: $verificationCode, isVerified: $isVerified, approved: $approved, blocked: $blocked, createdAt: $createdAt, updatedAt: $updatedAt, v: $v, password: $password, userSettingsModel: $userSettingsModel, newUser: $newUser, profilePicture: $profilePicture, homeAddress: $homeAddress, nextOfKin: $nextOfKin, diagnosisHistory: $diagnosisHistory, diagnosisDate: $diagnosisDate, aboutPatient: $aboutPatient}'; + } + + UserModel.fromJson(Map json){ + fcmTokens = FcmTokenModel.fromJson(json['fcm_tokens']??""); + locationModel = LocationModel.fromJson(json['location']??""); + id = json["_id"]??""; + userModelName = json["userModelName"]??""; + name = json["name"]??""; + version = json["version"]??''; + email = json["email"]??''; + phoneNumber = json["phoneNumber"]??''; + active = json["active"]??false; + role = json["role"]??''; + profilePictureUrl = json["profile_picture_url"]??''; + deviceId = json["deviceId"]??''; + verificationCode = json["verification_code"]??''; + isVerified = json["is_verified"]??false; + approved = json["approved"]??false; + blocked = json["blocked"]??false; + createdAt = json["createdAt"]??''; + updatedAt = json["updatedAt"]??''; + v = json["__v"]??-1; + password = json["password"]??''; + userSettingsModel = UserSettingsModel.fromJson(json["userSettings"]??""); + newUser = json['new_user']; + } + + Map toJson(){ + Map json = {}; + json['fcm_tokens'] = fcmTokens.toJson(); + json['location'] = locationModel.toJson(); + json["_id"] = id; + json["userModelName"] = userModelName; + json["name"] = name; + json["version"] = version; + json["email"] = email; + json["phoneNumber"] = phoneNumber; + json["active"] = active; + json["role"] = role; + json["profile_picture_url"] = profilePictureUrl; + json["deviceId"] = deviceId; + json["verification_code"] = verificationCode; + json["is_verified"] = isVerified ; + json["approved"] = approved ; + json["blocked"] = blocked ; + json["createdAt"] = createdAt; + json["updatedAt"] = updatedAt; + json["__v"] = v; + json["password"] = password; + json["userSettings"] = userSettingsModel.toJson(); + json['new_user'] = newUser; + return json; + } +} + +class FcmTokenModel{ + String token = ""; + String deviceType = ""; + + FcmTokenModel.empty(); + + FcmTokenModel.fromJson(Map json){ + token = json["token"] ?? ""; + deviceType = json["deviceType"] ?? ""; + } + + Map toJson(){ + Map json = {}; + json["token"] = token ; + json["deviceType"] = deviceType ; + return json; + } + + @override + String toString() { + return 'FcmTokenModel{token: $token, deviceType: $deviceType}'; + } +} + +class LocationModel{ + String type = ""; + List coordinates = []; + + LocationModel.empty(); + LocationModel.fromJson(Map json){ + type = json["type"]??""; + coordinates = json["coordinates"]??[]; + } + + double getLatitude(){return coordinates.first;} + double getLongitude(){return coordinates.last;} + + Map toJson(){ + Map json = {}; + json["type"] = type ; + json["coordinates"] = coordinates ; + return json; + } + + @override + String toString() { + return 'LocationModel{type: $type, coordinates: $coordinates}'; + } +} + +class UserSettingsModel{ + String id = ""; + String user = ""; + bool active = false; + String createdAt = ""; + String updatedAt = ""; + String v = ""; + NotificationSettings notificationSettings = NotificationSettings.empty(); + + UserSettingsModel.empty(); + + UserSettingsModel.fromJson(Map json){ + id = json["id"]??""; + user = json["user"]??""; + active = json["active"]??false; + createdAt = json["createdAt"]??""; + updatedAt = json["updatedAt"]??""; + v = json["v"]??""; + notificationSettings = + NotificationSettings.fromJson(json["notificationSettings"] ?? {"": ""}); + } + + Map toJson(){ + Map json = {}; + json["id"] = id; + json["user"] = user; + json["active"] = active; + json["createdAt"] = createdAt; + json["updatedAt"] = updatedAt; + json["v"] = v; + return json; + } + + @override + String toString() { + return 'UserSettingsModel{id: $id, user: $user, active: $active, createdAt: $createdAt, updatedAt: $updatedAt, v: $v, notificationSettings: $notificationSettings}'; + } +} + +class NotificationSettings{ + bool showActive = false; + bool pauseNotification = false; + String pauseDuration = ""; + String durationUnit = ""; + + NotificationSettings.empty(); + + NotificationSettings.fromJson(Map json){ + showActive = json["showActive"]??false; + pauseNotification = json["pauseNotification"]??false; + pauseDuration = json["pauseDuration"]??""; + durationUnit = json["durationUnit"]??""; + } + + Map toJson(){ + Map json = {}; + json["showActive"] = showActive; + json["pauseNotification"] = pauseNotification; + json["pauseDuration"] = pauseDuration; + json["durationUnit"] = durationUnit; + return json; + } + + @override + String toString() { + return 'NotificationSettings{showActive: $showActive, pauseNotification: $pauseNotification, pauseDuration: $pauseDuration, durationUnit: $durationUnit}'; + } +} \ No newline at end of file diff --git a/lib/utilities/app_session_manager.dart b/lib/utilities/app_session_manager.dart new file mode 100644 index 0000000..f5f07f5 --- /dev/null +++ b/lib/utilities/app_session_manager.dart @@ -0,0 +1,42 @@ +import 'package:ftc_mobile_app/dialogs/app_dialogs.dart'; +import 'package:get/get.dart'; +import 'package:stop_watch_timer/stop_watch_timer.dart'; + +class AppSessionManager extends GetxService { + static AppSessionManager instance = Get.find(); + + StopWatchTimer? _stopWatchTimer; + + startSessionTimer(int millis) async { + await _stopAndInitTimer(millis); + _stopWatchTimer?.onStartTimer(); + } + + _stopTimer() async { + _stopWatchTimer?.onStopTimer(); + await _stopWatchTimer?.dispose(); + _stopWatchTimer = null; + } + + _stopAndInitTimer(int millis) async { + _stopTimer(); + _initTimer(millis); + } + + _initTimer(int millis) { + _stopWatchTimer = StopWatchTimer( + mode: StopWatchMode.countDown, + presetMillisecond: millis, + onEnded: () { + _stopTimer(); + //Todo: Show session expire dialog and logout from app + + if (Get.isOverlaysOpen) Get.back(); + AppDialog.showUnauthorizedAlert(); + }); + } + + dispose() { + _stopWatchTimer?.dispose(); + } +} diff --git a/lib/utilities/assets_manager.dart b/lib/utilities/assets_manager.dart new file mode 100644 index 0000000..76974f0 --- /dev/null +++ b/lib/utilities/assets_manager.dart @@ -0,0 +1,155 @@ +class AssetsManager { + AssetsManager._(); + + static const String kAppIcon = "assets/images/png/app-logo-icon.png"; + static const String kLockIcon = "assets/images/svg/lock-icon.svg"; + static const String kCalendarIcon = + "assets/images/svg/nav_bar_calendar_icon.svg"; + static const String kHomeIcon = "assets/images/svg/nav_bar_home_icon.svg"; + static const String kMessageIcon = + "assets/images/svg/nav_bar_message_icon.svg"; + static const String kPeopleIcon = "assets/images/svg/nav_bar_people_icon.svg"; + static const String kPersonMainIcon = + "assets/images/svg/nav_bar_person_main_icon.svg"; + static const String kBackIcon = "assets/images/svg/back_icon.svg"; + static const String kCalendarAppointmentIcon = + "assets/images/svg/calendar_appointment_icon.svg"; + static const String kCarePlanIcon = "assets/images/svg/care_plan_icon.svg"; + static const String kNotesIcon = "assets/images/svg/notes_icon.svg"; + static const String kPhotoGalleryIcon = + "assets/images/svg/photo_gallery_icon.svg"; + static const String kGoToArrowIcon = + "assets/images/svg/got_to_arrow_button.svg"; + static const String kUploadIcon = "assets/images/svg/upload_icon.svg"; + static const String kFlagIcon = "assets/images/svg/flag_icon.svg"; + static const String kClockIcon = "assets/images/svg/clock_icon.svg"; + static const String kMicIcon = "assets/images/svg/microphone_mike.svg"; + static const String kBellIcon = "assets/images/svg/bell_icon.svg"; + static const String kManImage = "assets/images/svg/man_image.svg"; + static const String kManImagePng = "assets/images/svg/man_image_png.png"; + static const String kDrawerIcon = "assets/images/svg/menu_drawer_icon.svg"; + static const String kPeopleUnselectedIcon = + "assets/images/svg/people_unselected.svg"; + static const String kPoliciesIcon = "assets/images/svg/policies_icon.svg"; + static const String kSettingsIcon = "assets/images/svg/setting_icon.svg"; + static const String kSelectedCalendar = + "assets/images/svg/selected_calendar.svg"; + static const String kPlusIcon = "assets/images/svg/plus_icon.svg"; + static const String kArrowNextIcon = "assets/images/svg/arrow-next-icon.svg"; + static const String kPencilIcon = "assets/images/svg/pencil-icon.svg"; + static const String kFolderIcon = "assets/images/svg/folder_icon.svg"; + static const String kManBodyImage = "assets/images/svg/man_body_image.svg"; + static const String kPencilOutlineIcon = + "assets/images/svg/pencil_outline_icon.svg"; + static const String kEclipseIcon = "assets/images/svg/eclipse.svg"; + static const String kTriangleIcon = "assets/images/svg/triangle.svg"; + static const String kDustBinRedIcon = "assets/images/svg/dustbin_red.svg"; + + //care notes categories + static const String kIcGeneral = + "assets/images/svg/careNotesCategories/ic_general.svg"; + static const String kIcHealth = + "assets/images/svg/careNotesCategories/ic_health.svg"; + static const String kIcIndependentLiving = + "assets/images/svg/careNotesCategories/ic_independent_living.svg"; + static const String kIcIntractions = + "assets/images/svg/careNotesCategories/ic_intractions.svg"; + static const String kIcMentalWellbeing = + "assets/images/svg/careNotesCategories/ic_mental_wellbeing.svg"; + static const String kIcPersonalCare = + "assets/images/svg/careNotesCategories/ic_personal_care.svg"; + + //care notes Subcategories + static const String kIcAbc = + 'assets/images/svg/careNotesSubcatgeories/ic_abc.svg'; + static const String kIcActivity = + 'assets/images/svg/careNotesSubcatgeories/ic_activity.svg'; + static const String kIcAppointment = + 'assets/images/svg/careNotesSubcatgeories/ic_appointment.svg'; + static const String kIcCleaning = + 'assets/images/svg/careNotesSubcatgeories/ic_cleaning.svg'; + static const String kIcConsent = + 'assets/images/svg/careNotesSubcatgeories/ic_consent.svg'; + static const String kIcCooking = + 'assets/images/svg/careNotesSubcatgeories/ic_cooking.svg'; + static const String kIcEducation = + 'assets/images/svg/careNotesSubcatgeories/ic_education.svg'; + static const String kIcEmail = + 'assets/images/svg/careNotesSubcatgeories/ic_email.svg'; + static const String kIcFinance = + 'assets/images/svg/careNotesSubcatgeories/ic_finance.svg'; + static const String kIcHydration = + 'assets/images/svg/careNotesSubcatgeories/ic_hydration.svg'; + static const String kIcInjury = + 'assets/images/svg/careNotesSubcatgeories/ic_injury.svg'; + static const String kIcLaundry = + 'assets/images/svg/careNotesSubcatgeories/ic_laundry.svg'; + static const String kIcMeeting = + 'assets/images/svg/careNotesSubcatgeories/ic_meeting.svg'; + static const String kIcMood = + 'assets/images/svg/careNotesSubcatgeories/ic_mood.svg'; + static const String kIcMouthHygiene = + 'assets/images/svg/careNotesSubcatgeories/ic_mouth_hygiene.svg'; + static const String kIcNote = + 'assets/images/svg/careNotesSubcatgeories/ic_note.svg'; + static const String kIcOtherInteractions = + 'assets/images/svg/careNotesSubcatgeories/ic_other_interactions.svg'; + static const String kIcPhysicalIntervention = + 'assets/images/svg/careNotesSubcatgeories/ic_physical_intervention.svg'; + static const String kIcPublicInteraction = + 'assets/images/svg/careNotesSubcatgeories/ic_public_interaction.svg'; + static const String kIcReviews = + 'assets/images/svg/careNotesSubcatgeories/ic_reviews.svg'; + static const String kIcSafeguarding = + 'assets/images/svg/careNotesSubcatgeories/ic_safeguarding.svg'; + static const String kIcSearch = + 'assets/images/svg/careNotesSubcatgeories/ic_search.svg'; + static const String kIcShower = + 'assets/images/svg/careNotesSubcatgeories/ic_shower.svg'; + static const String kIcSleep = + 'assets/images/svg/careNotesSubcatgeories/ic_sleep.svg'; + static const String kIcTelephone = + 'assets/images/svg/careNotesSubcatgeories/ic_telephone.svg'; + static const String kIcToileting = + 'assets/images/svg/careNotesSubcatgeories/ic_toileting.svg'; + static const String kIcWeightHeight = + 'assets/images/svg/careNotesSubcatgeories/ic_weight_height.svg'; + static const String kIcOthers = + 'assets/images/svg/careNotesSubcatgeories/ic_others.svg'; + + //rating icons + static const String ratingsIcAngry = + 'assets/images/png/ratings/ic_angry.webp'; + static const String ratingsIcBored = + 'assets/images/png/ratings/ic_bored.webp'; + static const String ratingsIcCalm = 'assets/images/png/ratings/ic_calm.webp'; + static const String ratingsIcConfident = + 'assets/images/png/ratings/ic_confident.webp'; + static const String ratingsIcExcited = + 'assets/images/png/ratings/ic_excited.webp'; + static const String ratingsIcHappy = + 'assets/images/png/ratings/ic_happy.webp'; + static const String ratingsIcHopeful = + 'assets/images/png/ratings/ic_hopeful.webp'; + static const String ratingsIcNervous = + 'assets/images/png/ratings/ic_nervous.webp'; + static const String ratingsIcProud = + 'assets/images/png/ratings/ic_proud.webp'; + static const String ratingsIcRelaxed = + 'assets/images/png/ratings/ic_relaxed.webp'; + static const String ratingsIcSad = 'assets/images/png/ratings/ic_sad.webp'; + static const String ratingsIcScared = + 'assets/images/png/ratings/ic_scared.webp'; + static const String ratingsIcTired = + 'assets/images/png/ratings/ic_tired.webp'; + static const String ratingsIcWorried = + 'assets/images/png/ratings/ic_worried.webp'; + + static const String kConsentCapacityFormHtml = 'assets/consent-capacity.html'; + static const String svgHumanBodyFrontBack = 'assets/images/svg/human_body_front_back.svg'; + static const String pngHumanBodyFrontBack = 'assets/images/png/human_body_front_back.png'; + + static const String svgIcAdd = 'assets/images/svg/ic_add.svg'; + static const String svgIcAt = 'assets/images/svg/ic_at.svg'; + +} diff --git a/lib/utilities/constant_text.dart b/lib/utilities/constant_text.dart new file mode 100644 index 0000000..c663dfb --- /dev/null +++ b/lib/utilities/constant_text.dart @@ -0,0 +1,38 @@ +class ConstantText { + ConstantText._(); + + static const String kAppName = "FTC App"; + static const String kWelcomeBack = "Welcome Back!"; + static const String kPleaseLoginToContinue = "Please login to continue"; + static const String kPleaseInputEmail = "Please input your email"; + static const String kPleaseInputOTP = "Please input your otp"; + static const String kInvalidOTP = "Invalid otp"; + static const String kEmailHeading = "Email"; + static const String kPasswordHeading = "Password"; + static const String kEmailIsRequired = "Email is required"; + static const String kPasswordIsRequired = "Password is required"; + static const String kEmailPhoneIsRequired = "Email or Phone is required"; + static const String kInvalidEmail = "Invalid email"; + static const String kRememberMe = "Remember me"; + static const String kForgotPassword = "Forgot Password?"; + static const String kLogIn = "Log in"; + static const String kSubmit = "Submit"; + static const String kSave = "Save"; + static const String kSendCode = "Send code"; + static const String kAgencyLogin = "Agency log in"; + static const String kEmailOrPhoneHeading = "Email or Phone"; + static const String kInputEmailOrPhone = "Please input email or phone"; + static const String kOTPScreenMsg = "Enter the 6 digit code we sent to your email to log in. Your code is available for the next 12 hours."; + static const String kTwoFactorAuth = "2 Factor Authentication"; + static const String kTypeTitle = "Type Title"; + static const String kTitle = "Title"; + static const String kViewFullBodyMap = "View Full Body Map"; + static const String kFlagForHandover = "Flag for handover?"; + static const String kNoteDetails = "Note Details"; + static const String kNoteDetailsHint = "Enter note details here..."; + + + + static const String termsUrl = "https://ftcaresoftware.co.uk/terms-and-conditions.html"; + static const String privacyUrl = "https://ftcaresoftware.co.uk/app-privacy-policy.html"; +} \ No newline at end of file diff --git a/lib/utilities/custom_app_colors.dart b/lib/utilities/custom_app_colors.dart new file mode 100644 index 0000000..41438af --- /dev/null +++ b/lib/utilities/custom_app_colors.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +class CustomAppColors { + CustomAppColors._(); + + static const Color kTransparentColor = Colors.transparent; + static const Color kPrimaryColor = Color(0xFFFFFFFF); + static const Color kSecondaryColor = Color(0xFF278DEC); + static const Color kIconColor = Color(0xFF1D3F43); + static Color kLightGreyColor = Colors.grey.shade400; + static const Color kSmokeColor = Color(0xFFE9EBEC); + static const Color kLightTextColor = Color(0xFF9F9FA3); + static const Color kRedColor = Colors.red; + static const Color kDarkRedColor= Color(0xffDA0B0B); + static const Color kBlueColor= Color(0xff00577b); + static const Color kSeaGreenColor= Color(0xff00d194); + static const Color kLightPinkColor= Color(0xffff5a6b); + static const Color kBlackColor= Color(0xff000000); + static const Color kWhiteColor= Color(0xffffffff); + static const Color kGreenColor = Color(0xFF7dbe20); + static const Color kDarkGreenColor = Color(0xFF0AC247); + static const Color kDarkestGreenColor = Color(0xFF008000); + static const Color kDarkBlueTextColor = Color(0xff1D3F43); + static const Color kYellowColor = Color(0xffFFB800); + static const Color kDarkYellowColor = Color(0xffF2AE00); + +} diff --git a/lib/utilities/custom_router/custom_route_generator.dart b/lib/utilities/custom_router/custom_route_generator.dart new file mode 100644 index 0000000..b5443ed --- /dev/null +++ b/lib/utilities/custom_router/custom_route_generator.dart @@ -0,0 +1,555 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/view/screens/chat/arguments/chat_screen_args.dart'; +import 'package:ftc_mobile_app/view/screens/clients/addEditMemoryBox/add_edit_memory_box_screen.dart'; +import 'package:ftc_mobile_app/view/screens/clients/addEditRiskAssessment/add_edit_risk_assessment_screen.dart'; +import 'package:ftc_mobile_app/view/screens/clients/add_details_to_new_body_point_screen.dart'; +import 'package:ftc_mobile_app/view/screens/clients/add_new_document_screen.dart'; +import 'package:ftc_mobile_app/view/screens/clients/add_new_recent_incident_screen.dart'; +import 'package:ftc_mobile_app/view/screens/clients/all_care_notes_screen.dart'; +import 'package:ftc_mobile_app/view/screens/clients/careNoteForms/nutrition_hydration_form_screen.dart'; +import 'package:ftc_mobile_app/view/screens/clients/clients_new_view_module/add_new_pbs_plan_screen.dart'; +import 'package:ftc_mobile_app/view/screens/home/select_user_for_chat_screen.dart'; +import 'package:ftc_mobile_app/view/screens/settings/settings_screen.dart'; +import 'package:ftc_mobile_app/view/screens/training/training_detail_screen.dart'; +import 'package:ftc_mobile_app/view/screens/training/training_screen.dart'; +import 'package:ftc_mobile_app/view/screens/webview/webview_screen.dart'; +import '../../models/clients/allCareNotes/CarePlans.dart'; +import '../../view/screens/clients/careNoteForms/mood_rating_form.dart'; +import '../../view/screens/clients/care_note_detail_screen.dart'; + +class CustomRouteGenerator { + static GlobalKey navigatorKey = GlobalKey(); + + CustomRouteGenerator._(); + + static Object? argument; + + static Route? generateRoute(RouteSettings settings) { + debugPrint("Current Route: ${settings.name}"); + argument = settings.arguments; + switch (settings.name) { + // GetPage( + // name: CustomRouteNames.webviewScreen, + // page: () => WebviewScreen( + // args: Get.arguments as WebviewScreenArgument, + // ), + // binding: BindingsBuilder( + // () => Get.lazyPut(() => WebviewScreenController()))), + + case CustomRouteNames.kWebviewScreen: + return MaterialPageRoute( + builder: (context) { + return WebviewScreen( + args: settings.arguments as WebviewScreenArgument, + ); + }, + ); + case CustomRouteNames.kInitialRoute: + return MaterialPageRoute( + builder: (context) { + return const SplashScreen(); + }, + ); + case CustomRouteNames.kLoginScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const SignInScreen(); + }, + ); + case CustomRouteNames.kAgencySignInScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const AgencySignIn(); + }, + ); + case CustomRouteNames.kOTPScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const OTPScreen(); + }, + ); + case CustomRouteNames.kRotaDashboardScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const RotaDashboardScreen(); + }, + ); + // case CustomRouteNames.kCalendarScreenRoute: + // return MaterialPageRoute( + // builder: (context) { + // return CalendarScreen( + // controller: Get.put(CalendarViewScreenController()), + // ); + // }, + // ); + case CustomRouteNames.kBookHolidayScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const BookHolidayScreen(); + }, + ); + case CustomRouteNames.kClientsListScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const ClientsListScreen(); + }, + ); + case CustomRouteNames.kClientsProfileScreenRoute: + return MaterialPageRoute( + builder: (context) { + return ClientProfileScreen( + userData: settings.arguments as UserData, + ); + }, + ); + case CustomRouteNames.kAppointmentsScreenRoute: + return MaterialPageRoute( + builder: (context) { + return AppointmentScreen( + userData: settings.arguments as UserData, + ); + }, + ); + case CustomRouteNames.kNotesScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const NotesScreen(); + }, + ); + case CustomRouteNames.kSelectNoteScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const SelectNoteScreen(); + }, + ); + case CustomRouteNames.kNewNoteScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const NewNoteScreen(); + }, + ); + case CustomRouteNames.kCarePlanMenuScreenRoute: + return MaterialPageRoute( + builder: (context) { + return CarePlanMenuScreen( + userData: settings.arguments as UserData, + ); + }, + ); + case CustomRouteNames.kDashboardScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const DashboardScreen(); + }, + ); + case CustomRouteNames.kPickUpShiftsScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const PickUpShiftsScreen(); + }, + ); + case CustomRouteNames.kChatScreenRoute: + return MaterialPageRoute( + builder: (context) { + return ChatScreen( + args: settings.arguments as ChatScreenArgs, + ); + }, + ); + case CustomRouteNames.kNotificationListScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const NotificationListScreen(); + }, + ); + case CustomRouteNames.kViewProfileScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const ViewProfileScreen(); + }, + ); + case CustomRouteNames.kYourRotaScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const YourRotaScreen(); + }, + ); + case CustomRouteNames.kInboxScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const InboxScreen(); + }, + ); + case CustomRouteNames.kDocumentsListScreenRoute: + return MaterialPageRoute( + builder: (context) { + return DocumentsListScreen( + userData: settings.arguments as UserData, + ); + }, + ); + case CustomRouteNames.kDocumentDetailsScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const DocumentDetailsScreen(); + }, + ); + case CustomRouteNames.kRecentIncidentsScreenRoute: + return MaterialPageRoute( + builder: (context) { + return RecentIncidentsScreen( + userData: settings.arguments as UserData, + ); + }, + ); + case CustomRouteNames.kCurrentHealthIssuesScreenRoute: + return MaterialPageRoute( + builder: (context) { + return CurrentHealthIssuesScreen( + userData: settings.arguments as UserData, + ); + }, + ); + case CustomRouteNames.kConsentAndCapacityQuestionnaireScreenRoute: + return MaterialPageRoute( + builder: (context) { + return ConsentAndCapacityQuestionnaireScreen( + userData: settings.arguments as UserData, + ); + }, + ); + case CustomRouteNames.kConsentAndCapacityAddNewFormScreenRoute: + // ConsentDetailsModel consentDetailsModel = ConsentDetailsModel.empty(); + // if (settings.arguments != null && + // settings.arguments is ConsentDetailsModel && + // consentDetailsModel.description.isNotEmpty) { + // consentDetailsModel = settings.arguments as ConsentDetailsModel; + // } + return MaterialPageRoute( + builder: (context) { + return ConsentAndCapacityAddNewFormScreen( + args: + settings.arguments as ConsentAndCapacityAddNewFormScreenArgs, + ); + }, + ); + case CustomRouteNames.kLifeHistoryAndGoalsScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const LifeHistoryAndGoalsScreen(); + }, + ); + case CustomRouteNames.kPBSPlanScreenRoute: + return MaterialPageRoute( + builder: (context) { + return PBSPlanScreen( + userData: settings.arguments as UserData, + ); + }, + ); + case CustomRouteNames.kPhotoGalleryScreenRoute: + return MaterialPageRoute( + builder: (context) { + return PhotoGalleryScreen( + userData: settings.arguments as UserData, + ); + }, + ); + case CustomRouteNames.kRiskAssessmentsScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const RiskAssessmentsScreen(); + }, + ); + case CustomRouteNames.kRiskAssessmentsTemplateScreenRoute: + return MaterialPageRoute( + builder: (context) { + return RiskAssessmentsTemplateScreen( + userData: settings.arguments as UserData, + ); + }, + ); + case CustomRouteNames.kSupportPlanScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const SupportPlanScreen(); + }, + ); + case CustomRouteNames.kOverViewScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const OverviewScreen(); + }, + ); + case CustomRouteNames.kCrisisManagementScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const CrisisManagementScreen(); + }, + ); + case CustomRouteNames.kMentalHealthScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const MentalHealthScreen(); + }, + ); + case CustomRouteNames.kHealthScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const HealthScreen(); + }, + ); + case CustomRouteNames.kFuturePlansScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const FuturePlansScreen(); + }, + ); + case CustomRouteNames.kMyCurrentPlanScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const MyCurrentPlanScreen(); + }, + ); + case CustomRouteNames.kMyInterestsScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const MyInterestsScreen(); + }, + ); + case CustomRouteNames.kThingsIWantYouToHelpScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const ThingsIWantYouToHelpScreen(); + }, + ); + case CustomRouteNames.kClientsIntroductionScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const ClientIntroductionScreen(); + }, + ); + case CustomRouteNames.kMedicationScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const MedicationScreen(); + }, + ); + case CustomRouteNames.kHealthFullBodyMapScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const HealthFullBodyMapScreen(); + }, + ); + case CustomRouteNames.kAddDetailsToNewPointScreenRoute: + return MaterialPageRoute( + builder: (context) { + return AddDetailsToNewBodyPointScreen( + args: settings.arguments as AddDetailsToNewBodyPointScreenArgs, + ); + }, + ); + case CustomRouteNames.kAddNewRecentIncidentsScreenRoute: + return MaterialPageRoute( + builder: (context) { + return AddNewRecentIncidentsScreen( + args: settings.arguments as AddNewRecentIncidentsScreenArgs, + ); + }, + ); + case CustomRouteNames.kAddNewPBSPlanScreenRoute: + return MaterialPageRoute( + builder: (context) { + return AddNewPBSPlanScreen( + args: settings.arguments as AddNewPBSPlanScreenArgs, + ); + }, + ); + case CustomRouteNames.kAddNewDocumentScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const AddNewDocumentScreen(); + }, + ); + case CustomRouteNames.kSelectUserForChatScreenRoute: + return MaterialPageRoute( + builder: (context) { + return const SelectUserForChatScreen(); + }, + ); + case CustomRouteNames.kCareNotesScreenRoute: + return MaterialPageRoute( + builder: (context) { + return CareNotesScreen( + args: settings.arguments as CareNotesScreenArgs, + ); + }, + ); + case CustomRouteNames.kCareNotesSubcategoriesScreenRoute: + return MaterialPageRoute( + builder: (context) { + return CareNotesSubcategoriesScreen( + args: settings.arguments as CareNotesSubcategoriesScreenArgs, + ); + }, + ); + case CustomRouteNames.kFreeTextEntriesScreenRoute: + return MaterialPageRoute( + builder: (context) { + return FreeTextEntriesFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kWeightHeightFormScreenRoute: + return MaterialPageRoute( + builder: (context) { + return WeightHeightFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kHealthAppointmentsFormScreenRoute: + return MaterialPageRoute( + builder: (context) { + return HealthAppointmentsFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kShoweringBathFormScreenRoute: + return MaterialPageRoute( + builder: (context) { + return ShoweringBathFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kToiletingNoteFormScreenRoute: + return MaterialPageRoute( + builder: (context) { + return ToiletingNoteFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kMoodRatingFormScreenRoute: + return MaterialPageRoute( + builder: (context) { + return MoodRatingFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kABCFormScreenRoute: + return MaterialPageRoute( + builder: (context) { + return ABCFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kConsentCapacityFormScreenRoute: + return MaterialPageRoute( + builder: (context) { + return ConsentCapacityFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kInjuryHealthIssueFormScreenRoute: + return MaterialPageRoute( + builder: (context) { + return InjuryHealthIssueFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kObservationsFormScreenRoute: + return MaterialPageRoute( + builder: (context) { + return ObservationsFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kSafeguardingFormScreenRoute: + return MaterialPageRoute( + builder: (context) { + return SafeguardingFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kPhysicalInterventionFormScreenRoute: + return MaterialPageRoute( + builder: (context) { + return PhysicalInterventionFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kNutritionHydrationFormScreenRoute: + return MaterialPageRoute( + builder: (context) { + return NutritionHydrationFormScreen( + args: settings.arguments as CommonCareNoteFormArgs, + ); + }, + ); + case CustomRouteNames.kAllCareNotesScreenRoute: + return MaterialPageRoute( + builder: (context) { + return AllCareNotesScreen( + args: settings.arguments as AllCareNotesScreenArgs, + ); + }, + ); + case CustomRouteNames.kCareNoteDetailScreenRoute: + return MaterialPageRoute( + builder: (_) { + return CareNoteDetailScreen(data: settings.arguments as CarePlan); + }, + fullscreenDialog: true); + case CustomRouteNames.kAddEditMemoryBoxScreen: + return MaterialPageRoute( + builder: (_) { + return AddEditMemoryBoxScreen( + args: settings.arguments as AddEditMemoryBoxScreenArgs); + }, + fullscreenDialog: true); + case CustomRouteNames.kAddEditRiskAssessmentScreen: + return MaterialPageRoute( + builder: (_) { + return AddEditRiskAssessmentScreen( + args: settings.arguments as AddEditRiskAssessmentScreenArgs); + }, + fullscreenDialog: true); + case CustomRouteNames.kSettingsScreen: + return MaterialPageRoute( + builder: (_) { + return const SettingsScreen(); + }, + ); + case CustomRouteNames.kTrainingsScreen: + return MaterialPageRoute( + builder: (_) { + return const TrainingsScreen(); + }, + ); + case CustomRouteNames.kTrainingDetailScreen: + return MaterialPageRoute( + builder: (_) { + return TrainingDetailScreen( + args: settings.arguments as TrainingDetailScreenArgs); + }, + fullscreenDialog: true); + + default: + return null; + } + } +} diff --git a/lib/utilities/custom_router/custom_route_names.dart b/lib/utilities/custom_router/custom_route_names.dart new file mode 100644 index 0000000..3ba39ba --- /dev/null +++ b/lib/utilities/custom_router/custom_route_names.dart @@ -0,0 +1,77 @@ +class CustomRouteNames { + CustomRouteNames._(); + + static const String kWebviewScreen = "/WebviewScreen"; + static const String kInitialRoute = "/"; + static const String kLoginScreenRoute = "/LoginScreenRoute"; + static const String kAgencySignInScreenRoute = "/AgencySignInScreenRoute"; + static const String kOTPScreenRoute = "/OTPScreenRoute"; + // static const String kCalendarScreenRoute = "/CalendarScreenRoute"; + static const String kRotaDashboardScreenRoute = "/RotaDashboardScreenRoute"; + static const String kBookHolidayScreenRoute = "/BookHolidayScreenRoute"; + static const String kClientsListScreenRoute = "/ClientsListScreenRoute"; + static const String kClientsProfileScreenRoute = "/ClientsProfileScreenRoute"; + static const String kAppointmentsScreenRoute = "/AppointmentsScreenRoute"; + static const String kNotesScreenRoute = "/NotesScreenRoute"; + static const String kSelectNoteScreenRoute = "/SelectNoteScreenRoute"; + static const String kNewNoteScreenRoute = "/NewNoteScreenRoute"; + static const String kCarePlanMenuScreenRoute = "/CarePlanMenuScreenRoute"; + static const String kDashboardScreenRoute = "/DashboardScreenRoute"; + static const String kPickUpShiftsScreenRoute = "/PickUpShiftScreenRoute"; + static const String kChatScreenRoute = "/ChatScreenRoute"; + static const String kNotificationListScreenRoute = "/NotificationListScreenRoute"; + static const String kViewProfileScreenRoute = "/ViewProfileScreenRoute"; + static const String kYourRotaScreenRoute = "/YourRotaScreenRoute"; + static const String kInboxScreenRoute = "/InboxScreenRoute"; + static const String kDocumentsListScreenRoute = "/kDocumentsListScreenRoute"; + static const String kDocumentDetailsScreenRoute = "/kDocumentDetailsScreenRoute"; + static const String kRecentIncidentsScreenRoute = "/kRecentIncidentsScreenRoute"; + static const String kCurrentHealthIssuesScreenRoute = "/kCurrentHealthIssuesScreenRoute"; + static const String kConsentAndCapacityQuestionnaireScreenRoute = "/kConsentAndCapacityQuestionnaireScreenRoute"; + static const String kConsentAndCapacityAddNewFormScreenRoute = "/kConsentAndCapacityAddNewFormScreenRoute"; + static const String kLifeHistoryAndGoalsScreenRoute = "/kLifeHistoryAndGoalsScreenRoute"; + static const String kPBSPlanScreenRoute = "/kPBSPlanScreenRoute"; + static const String kPhotoGalleryScreenRoute = "/kPhotoGalleryScreenRoute"; + static const String kRiskAssessmentsScreenRoute = "/kRiskAssessmentsScreenRoute"; + static const String kRiskAssessmentsTemplateScreenRoute = "/kRiskAssessmentsTemplateScreenRoute"; + static const String kSupportPlanScreenRoute = "/kSupportPlanScreenRoute"; + static const String kOverViewScreenRoute = "/kOverViewScreenRoute"; + static const String kCrisisManagementScreenRoute = "/kCrisisManagementScreenRoute"; + static const String kMentalHealthScreenRoute = "/kMentalHealthScreenRoute"; + static const String kHealthScreenRoute = "/kHealthScreenRoute"; + static const String kFuturePlansScreenRoute = "/kFuturePlansScreenRoute"; + static const String kMyCurrentPlanScreenRoute = "/kMyCurrentPlanScreenRoute"; + static const String kMyInterestsScreenRoute = "/kMyInterestsScreenRoute"; + static const String kThingsIWantYouToHelpScreenRoute = "/kThingsIWantYouToHelpScreenRoute"; + static const String kClientsIntroductionScreenRoute = "/kClientsIntroductionScreenRoute"; + static const String kMedicationScreenRoute = "/kMedicationScreenRoute"; + static const String kHealthFullBodyMapScreenRoute = "/kHealthFullBodyMapScreenRoute"; + static const String kAddDetailsToNewPointScreenRoute = "/kAddDetailsToNewPoint"; + static const String kAddNewRecentIncidentsScreenRoute = "/kAddNewRecentIncidentsScreenRoute"; + static const String kAddNewPBSPlanScreenRoute = "/kAddNewPBSPlanScreenRoute"; + static const String kAddNewDocumentScreenRoute = "/kAddNewDocumentScreenRoute"; + static const String kSelectUserForChatScreenRoute = "/kSelectUserForChatScreenRoute"; + + static const String kCareNotesScreenRoute = "/kCareNotesScreenRoute"; + static const String kCareNotesSubcategoriesScreenRoute = "/kCareNotesSubcategoriesScreenRoute"; + static const String kFreeTextEntriesScreenRoute = "/kFreeTextEntriesScreenRoute"; + static const String kWeightHeightFormScreenRoute = "/kWeightHeightFormScreenRoute"; + static const String kHealthAppointmentsFormScreenRoute = "/kHealthAppointmentsFormScreenRoute"; + static const String kShoweringBathFormScreenRoute = "/kShoweringBathFormScreenRoute"; + static const String kToiletingNoteFormScreenRoute = "/kToiletingNoteFormScreenRoute"; + static const String kMoodRatingFormScreenRoute = "/kMoodRatingFormScreenRoute"; + static const String kABCFormScreenRoute = "/kABCFormScreenRoute"; + static const String kConsentCapacityFormScreenRoute = "/kConsentCapacityFormScreenRoute"; + static const String kInjuryHealthIssueFormScreenRoute = "/kInjuryHealthIssueFormScreenRoute"; + static const String kObservationsFormScreenRoute = "/kObservationsFormScreenRoute"; + static const String kSafeguardingFormScreenRoute = "/kSafeguardingFormScreenRoute"; + static const String kPhysicalInterventionFormScreenRoute = "/kPhysicalInterventionFormScreenRoute"; + static const String kNutritionHydrationFormScreenRoute = "/kNutritionHydrationFormScreenRoute"; + static const String kAllCareNotesScreenRoute = "/kAllCareNotesScreenRoute"; + static const String kCareNoteDetailScreenRoute = "/kCareNoteDetailScreenRoute"; + static const String kAddEditMemoryBoxScreen = "/kAddEditMemoryBoxScreen"; + static const String kAddEditRiskAssessmentScreen = "/kAddEditRiskAssessmentScreen"; + static const String kSettingsScreen = "/kSettingsScreen"; + static const String kTrainingsScreen = "/kTrainingsScreen"; + static const String kTrainingDetailScreen = "/kTrainingDetailScreen"; +} \ No newline at end of file diff --git a/lib/utilities/custom_router/export_custom_router.dart b/lib/utilities/custom_router/export_custom_router.dart new file mode 100644 index 0000000..eb51a87 --- /dev/null +++ b/lib/utilities/custom_router/export_custom_router.dart @@ -0,0 +1,2 @@ +export 'custom_route_names.dart'; +export 'custom_route_generator.dart'; \ No newline at end of file diff --git a/lib/utilities/custom_theme.dart b/lib/utilities/custom_theme.dart new file mode 100644 index 0000000..4412e79 --- /dev/null +++ b/lib/utilities/custom_theme.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomTheme { + CustomTheme._(); + + static ThemeData defaultTheme() { + return ThemeData( + useMaterial3: true, + fontFamily: "Roboto", + colorScheme: const ColorScheme.light( + primary: CustomAppColors.kSecondaryColor + ), + progressIndicatorTheme: const ProgressIndicatorThemeData( + color: CustomAppColors.kSecondaryColor), + checkboxTheme: CheckboxThemeData( + shape: RoundedRectangleBorder( + side: const BorderSide( + color: CustomAppColors.kSmokeColor, + width: 0.6, + ), + borderRadius: BorderRadius.circular( + 5.r, + ), + ), + ), + // radioTheme: RadioThemeData( + // fillColor: MaterialStateProperty.resolveWith((states) { + // if (states.contains(MaterialState.pressed)) { + // return CustomAppColors.kSecondaryColor; + // } + // return Colors.white; + // }), + // ), + splashColor: CustomAppColors.kTransparentColor, + ); + } +} diff --git a/lib/utilities/custom_timeago_messages.dart b/lib/utilities/custom_timeago_messages.dart new file mode 100644 index 0000000..c6dd16c --- /dev/null +++ b/lib/utilities/custom_timeago_messages.dart @@ -0,0 +1,33 @@ +import 'package:get_time_ago/get_time_ago.dart'; + +class CustomTimeAgoMessages implements Messages { + @override + String prefixAgo() => ''; + + @override + String suffixAgo() => ''; + + @override + String secsAgo(int seconds) => 'a moment ago'; + + @override + String minAgo(int minutes) => 'a minute ago'; + + @override + String minsAgo(int minutes) => '$minutes minutes ago'; + + @override + String hourAgo(int minutes) => 'an hour ago'; + + @override + String hoursAgo(int hours) => '$hours hours ago'; + + @override + String dayAgo(int hours) => 'yesterday'; + + @override + String daysAgo(int days) => '$days days ago'; + + @override + String wordSeparator() => ' '; +} diff --git a/lib/utilities/custom_ui_over_lay.dart b/lib/utilities/custom_ui_over_lay.dart new file mode 100644 index 0000000..fd162ac --- /dev/null +++ b/lib/utilities/custom_ui_over_lay.dart @@ -0,0 +1,31 @@ +import 'package:flutter/services.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomUIOverLay { + CustomUIOverLay._(); + + static Future initialize() async { + _appStatusBarNavBarTheme(); + _setSystemOrientation(); + } + + static void _appStatusBarNavBarTheme() { + SystemChrome.setSystemUIOverlayStyle( + const SystemUiOverlayStyle( + systemNavigationBarColor: CustomAppColors.kTransparentColor, + systemNavigationBarDividerColor: CustomAppColors.kPrimaryColor, + systemNavigationBarIconBrightness: Brightness.dark, + statusBarIconBrightness: Brightness.dark, + statusBarBrightness: Brightness.light, + statusBarColor: CustomAppColors.kTransparentColor, + ), + ); + } + + static void _setSystemOrientation() { + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + // DeviceOrientation.portraitDown, + ]); + } +} diff --git a/lib/utilities/date_formatter.dart b/lib/utilities/date_formatter.dart new file mode 100644 index 0000000..e1f68bc --- /dev/null +++ b/lib/utilities/date_formatter.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class DateFormatter { + static final dateFormatter = DateFormat("dd-MM-yyyy"); + static final dateFormatter2 = DateFormat("dd/MM/yyyy"); + + String todayTomorrowYesterday(DateTime dateToCheck) { + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final yesterday = DateTime(now.year, now.month, now.day - 1); + final tomorrow = DateTime(now.year, now.month, now.day + 1); + + final aDate = + DateTime(dateToCheck.year, dateToCheck.month, dateToCheck.day); + if (aDate == today) { + return 'Today'; + } else if (aDate == yesterday) { + return 'Yesterday'; + } else if (aDate == tomorrow) { + return 'Tomorrow'; + } else { + return 'nothing'; + } + } + + // String getRotaHeadingDate(DateTime dateTime) { + // String wordDate = todayTomorrowYesterday(dateTime); + // String date = DateFormat("yMMMMd").format(dateTime); + // return wordDate != 'nothing' + // ? "$wordDate, ${date.split(" ").first.substring(0, 3)} ${dateTime.day}" + // : "${date.split(" ").first.substring(0, 3)} ${dateTime.day}"; + // } + + String getRotaDate(DateTime dateTime) { + //Nov 1, 5AM-0PM + String date = DateFormat("yMMMMd").format(dateTime); + return "${date.split(" ").first.substring(0, 3)} ${dateTime.day}, ${dateTime.hour}AM-${dateTime.minute}PM"; + } + + DateTime? time24to12formatDate({required String time}) { + try { + if (time.isEmpty) { + return null; + } + final f = DateFormat("HH:mm"); + return f.parse(time); + } catch (e) { + debugPrint(e.toString()); + return null; + } + } + + TimeOfDay? time24to12format({required String time}) { + final dateTime = time24to12formatDate(time: time); + return (dateTime == null) ? null : TimeOfDay.fromDateTime(dateTime); + } + + String roasterShiftFormattedTime({required String time}) { + final dateTime = time24to12formatDate(time: time); + return (dateTime == null) ? "" : DateFormat("hh:mm aa").format(dateTime); + } + + String getRotaNewDate( + {required int shiftDate, + required String startShiftTime, + required String endShiftTime}) { + if (shiftDate == 0 || startShiftTime.isEmpty || endShiftTime.isEmpty) { + return ""; + } + + final f = DateFormat("HH:mm"); + final sd = f.parse(startShiftTime); + final ed = f.parse(endShiftTime); + final date = DateTime.fromMillisecondsSinceEpoch(shiftDate); + + return "${DateFormat("MMM dd").format(date)}, ${DateFormat("hha").format(sd)} - ${DateFormat("hha").format(ed)}"; + } + + String getAppointmentTime(DateTime dateTime) { + //Nov 1 + String date = DateFormat("yMMMMd").format(dateTime); + return "${date.split(" ").first.substring(0, 3)} ${dateTime.day}"; + } + + String getHolidayDate(DateTime dateTime) { + //jan 2, 2024 + String date = DateFormat("MMM dd, yyyy").format(dateTime); + return "${date.split(" ").first.substring(0, 3)} ${dateTime.day}, ${dateTime.year}"; + } + + String getFormattedDateFromUtc(String utcTime) { + try { + return DateFormat("yyyy-MM-dd") + .parse(utcTime, true) + .toLocal() + .toString() + .split(" ") + .first; //String 2024-02-22 + } catch (e) { + return ""; + } + } + + static String ddMMyyyyhhmmFormat(DateTime date) { + return DateFormat("dd/MM/yyyy hh:mm aa").format(date); + } +} diff --git a/lib/utilities/enums/api_method.dart b/lib/utilities/enums/api_method.dart new file mode 100644 index 0000000..9edafae --- /dev/null +++ b/lib/utilities/enums/api_method.dart @@ -0,0 +1,9 @@ +enum ApiMethod { + get, + post, + put, + patch, + delete; + + String get value => name; +} \ No newline at end of file diff --git a/lib/utilities/enums/body_parts.dart b/lib/utilities/enums/body_parts.dart new file mode 100644 index 0000000..627668b --- /dev/null +++ b/lib/utilities/enums/body_parts.dart @@ -0,0 +1,54 @@ +enum BodyPart { + fronthead(topPercent: 11, leftPercent: 19.5, apiValue: 'fronthead'), + backhead(topPercent: 11, leftPercent: 77.5, apiValue: 'backhead'), + righteye(topPercent: 14, leftPercent: 17, apiValue: 'righteye'), + lefteye(topPercent: 14, leftPercent: 22, apiValue: 'lefteye'), + rightear(topPercent: 15, leftPercent: 14, apiValue: 'rightear'), + leftear(topPercent: 15, leftPercent: 25.5, apiValue: 'leftear'), + nose(topPercent: 16, leftPercent: 19.5, apiValue: 'nose'), + mouth(topPercent: 18, leftPercent: 19.5, apiValue: 'mouth'), + neck(topPercent: 21, leftPercent: 19.5, apiValue: 'neck'), + nape(topPercent: 20, leftPercent: 77.5, apiValue: 'nape'), + rightshoulder(topPercent: 26, leftPercent: 7.5, apiValue: 'rightshoulder'), + leftshoulder(topPercent: 26, leftPercent: 31.5, apiValue: 'leftshoulder'), + rightshoulderblade(topPercent: 26, leftPercent: 88.5, apiValue: 'rightshoulderblade'), + leftshoulderblade(topPercent: 26, leftPercent: 66.5, apiValue: 'leftshoulderblade'), + rightArm(topPercent: 35, leftPercent: 5.5, apiValue: 'rightArm'), + leftArm(topPercent: 35, leftPercent: 33.5, apiValue: 'leftArm'), + rightforearm(topPercent: 46, leftPercent: 4.5, apiValue: 'rightforearm'), + leftforearm(topPercent: 46, leftPercent: 34.5, apiValue: 'leftforearm'), + rightelbow(topPercent: 41, leftPercent: 93, apiValue: 'rightelbow'), + leftelbow(topPercent: 41, leftPercent: 62, apiValue: 'leftelbow'), + rightwrist(topPercent: 51, leftPercent: 3, apiValue: 'rightwrist'), + leftwrist(topPercent: 51, leftPercent: 36, apiValue: 'leftwrist'), + righthand(topPercent: 55, leftPercent: 3, apiValue: 'righthand'), + lefthand(topPercent: 55, leftPercent: 36, apiValue: 'lefthand'), + chest(topPercent: 30, leftPercent: 19.5, apiValue: 'chest'), + topback(topPercent: 30, leftPercent: 77.5, apiValue: 'topback'), + middleback(topPercent: 38, leftPercent: 77.5, apiValue: 'middleback'), + bottomback(topPercent: 47, leftPercent: 77.5, apiValue: 'bottomback'), + abdomen(topPercent: 40, leftPercent: 19.5, apiValue: 'abdomen'), + lefthip(topPercent: 54, leftPercent: 67, apiValue: 'lefthip'), + righthip(topPercent: 54, leftPercent: 88.3, apiValue: 'righthip'), + rightbuttock(topPercent: 52, leftPercent: 83, apiValue: 'rightbuttock'), + leftbuttock(topPercent: 52, leftPercent: 72, apiValue: 'leftbuttock'), + groin(topPercent: 54, leftPercent: 19.5, apiValue: 'groin'), + rightthigh(topPercent: 61, leftPercent: 13.5, apiValue: 'rightthigh'), + leftthigh(topPercent: 61, leftPercent: 25.5, apiValue: 'leftthigh'), + rightknee(topPercent: 73, leftPercent: 13.5, apiValue: 'rightknee'), + leftknee(topPercent: 73, leftPercent: 25.5, apiValue: 'leftknee'), + rightcalf(topPercent: 81, leftPercent: 84, apiValue: 'rightcalf'), + leftcalf(topPercent: 81, leftPercent: 71.5, apiValue: 'leftcalf'), + leftankle(topPercent: 93, leftPercent: 25, apiValue: 'leftankle'), + rightankle(topPercent: 93, leftPercent: 14.5, apiValue: 'rightankle'), + leftfoot(topPercent: 96, leftPercent: 25, apiValue: 'leftfoot'), + rightfoot(topPercent: 96, leftPercent: 14.5, apiValue: 'rightfoot'), + leftheel(topPercent: 94, leftPercent: 72.5, apiValue: 'leftheel'), + rightheel(topPercent: 94, leftPercent: 83, apiValue: 'rightheel'); + + final double topPercent; + final double leftPercent; + final String apiValue; + + const BodyPart({required this.topPercent, required this.leftPercent, required this.apiValue}); +} \ No newline at end of file diff --git a/lib/utilities/enums/care_note_form_type.dart b/lib/utilities/enums/care_note_form_type.dart new file mode 100644 index 0000000..2d0b799 --- /dev/null +++ b/lib/utilities/enums/care_note_form_type.dart @@ -0,0 +1,120 @@ +///Note: when adding new type, make sure to add condition in [CareNotesFormType.fromText] also +enum CareNotesFormType { + injuryHealthIssueForm("injuryHealthIssueForm", "InjuryHealthIssue"), + weightHeightForm("weightHeightForm", "WeightHeight"), + healthAppointmentForm("healthAppointmentForm", "HealthAppointments"), + observationsForm("observationsForm", "Observations"), + healthOtherForm("healthOtherForm", 'HealthOther'), + generalNoteForm("generalNoteForm", "GeneralNote"), + activitiesForm("activitiesForm", "Activities"), + sleepForm("sleepForm", "Sleep"), + safeguardingForm("safeguardingForm", "Safeguarding"), + generalOtherForm("generalOtherForm", "GeneralOther"), + toiletingNoteForm("toiletingNoteForm", "Toileting"), + showeringBathForm("showeringBathForm", "ShoweringBath"), + mouthHygieneForm("mouthHygieneForm", "MouthHygiene"), + personalCareOtherForm("personalCareOtherForm", "PersonalCareOther"), + moodRatingForm("moodRatingForm", "MoodRating"), + ABCForm("ABCForm", "ABC"), + physicalInterventionForm("physicalInterventionForm", "PhysicalIntervention"), + consentCapacityForm("consentCapacityForm", "ConsentCapacityMCADOLS"), + mentalWellbeingOtherForm("mentalWellbeingOtherForm", "MentalWellbeingOther"), + meetingsForm("meetingsForm", "Meetings"), + telephoneCallsForm("telephoneCallsForm", "TelephoneCalls"), + reviewsForm("reviewsForm", "Reviews"), + emailsForm("emailsForm", "Emails"), + allOtherInteractionsForm("allOtherInteractionsForm", "AllOtherInteractions"), + professionalFamilyInteractionsOtherForm( + "professionalFamilyInteractionsOtherForm", + "ProfessionalFamilyInteractionsOther"), + laundryForm("laundryForm", "Laundry"), + cookingForm("cookingForm", "Cooking"), + nutritionHydrationForm("nutritionHydrationForm", "NutritionHydration"), + cleaningForm("cleaningForm", "Cleaning"), + financeForm("financeForm", "Finance"), + publicInteractionForm("publicInteractionForm", "PublicInteraction"), + educationForm("educationForm", "Education"), + independentLivingOtherForm( + "independentLivingOtherForm", "IndependentLivingOther"), + ; + + final String text; + final String apiValue; + + const CareNotesFormType(this.text, this.apiValue); + + factory CareNotesFormType.fromText(String text) { + if (text == CareNotesFormType.injuryHealthIssueForm.text) { + return CareNotesFormType.injuryHealthIssueForm; + } else if (text == CareNotesFormType.weightHeightForm.text) { + return CareNotesFormType.weightHeightForm; + } else if (text == CareNotesFormType.healthAppointmentForm.text) { + return CareNotesFormType.healthAppointmentForm; + } else if (text == CareNotesFormType.observationsForm.text) { + return CareNotesFormType.observationsForm; + } else if (text == CareNotesFormType.healthOtherForm.text) { + return CareNotesFormType.healthOtherForm; + } else if (text == CareNotesFormType.generalNoteForm.text) { + return CareNotesFormType.generalNoteForm; + } else if (text == CareNotesFormType.activitiesForm.text) { + return CareNotesFormType.activitiesForm; + } else if (text == CareNotesFormType.sleepForm.text) { + return CareNotesFormType.sleepForm; + } else if (text == CareNotesFormType.safeguardingForm.text) { + return CareNotesFormType.safeguardingForm; + } else if (text == CareNotesFormType.generalOtherForm.text) { + return CareNotesFormType.generalOtherForm; + } else if (text == CareNotesFormType.showeringBathForm.text) { + return CareNotesFormType.showeringBathForm; + } else if (text == CareNotesFormType.mouthHygieneForm.text) { + return CareNotesFormType.mouthHygieneForm; + } else if (text == CareNotesFormType.personalCareOtherForm.text) { + return CareNotesFormType.personalCareOtherForm; + } else if (text == CareNotesFormType.moodRatingForm.text) { + return CareNotesFormType.moodRatingForm; + } else if (text == CareNotesFormType.toiletingNoteForm.text) { + return CareNotesFormType.toiletingNoteForm; + } else if (text == CareNotesFormType.ABCForm.text) { + return CareNotesFormType.ABCForm; + } else if (text == CareNotesFormType.physicalInterventionForm.text) { + return CareNotesFormType.physicalInterventionForm; + } else if (text == CareNotesFormType.consentCapacityForm.text) { + return CareNotesFormType.consentCapacityForm; + } else if (text == CareNotesFormType.mentalWellbeingOtherForm.text) { + return CareNotesFormType.mentalWellbeingOtherForm; + } else if (text == CareNotesFormType.meetingsForm.text) { + return CareNotesFormType.meetingsForm; + } else if (text == CareNotesFormType.telephoneCallsForm.text) { + return CareNotesFormType.telephoneCallsForm; + } else if (text == CareNotesFormType.reviewsForm.text) { + return CareNotesFormType.reviewsForm; + } else if (text == CareNotesFormType.emailsForm.text) { + return CareNotesFormType.emailsForm; + } else if (text == CareNotesFormType.allOtherInteractionsForm.text) { + return CareNotesFormType.allOtherInteractionsForm; + } else if (text == + CareNotesFormType.professionalFamilyInteractionsOtherForm.text) { + return CareNotesFormType.professionalFamilyInteractionsOtherForm; + } else if (text == CareNotesFormType.laundryForm.text) { + return CareNotesFormType.laundryForm; + } else if (text == CareNotesFormType.cookingForm.text) { + return CareNotesFormType.cookingForm; + } else if (text == CareNotesFormType.nutritionHydrationForm.text) { + return CareNotesFormType.nutritionHydrationForm; + } else if (text == CareNotesFormType.cleaningForm.text) { + return CareNotesFormType.cleaningForm; + } else if (text == CareNotesFormType.financeForm.text) { + return CareNotesFormType.financeForm; + } else if (text == CareNotesFormType.publicInteractionForm.text) { + return CareNotesFormType.publicInteractionForm; + } else if (text == CareNotesFormType.educationForm.text) { + return CareNotesFormType.educationForm; + } else if (text == CareNotesFormType.independentLivingOtherForm.text) { + return CareNotesFormType.independentLivingOtherForm; + } else { + throw ArgumentError( + '$text doesn\'t match any CareNotesFormTypes. Add following condition in CareNotesFormType.fromText factory constructor: \nelse if (text == CareNotesFormType.$text.text) {return CareNotesFormType.$text;}', + "text"); + } + } +} diff --git a/lib/utilities/export_utilities.dart b/lib/utilities/export_utilities.dart new file mode 100644 index 0000000..1a80ca0 --- /dev/null +++ b/lib/utilities/export_utilities.dart @@ -0,0 +1,11 @@ +export 'custom_router/export_custom_router.dart'; +export 'mixins/export_mixins.dart'; +export 'extensions/export_extensions.dart'; +export 'local_storage_manager/export_local_storage.dart'; +export 'constant_text.dart'; +export 'custom_theme.dart'; +export 'custom_app_colors.dart'; +export 'custom_ui_over_lay.dart'; +export 'assets_manager.dart'; +export 'frequent_functions.dart'; +export 'date_formatter.dart'; \ No newline at end of file diff --git a/lib/utilities/extensions/custom_extensions.dart b/lib/utilities/extensions/custom_extensions.dart new file mode 100644 index 0000000..33f09ab --- /dev/null +++ b/lib/utilities/extensions/custom_extensions.dart @@ -0,0 +1,200 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/loading_widget.dart'; +import 'package:get/get.dart'; + +//BuildContext +extension BuildContextExtensions on BuildContext { + showErrorSnackBar({required String message, Duration? duration}) { + try { + ScaffoldMessenger.of(this).showSnackBar(SnackBar( + content: Text(message), + backgroundColor: Colors.red, + duration: duration ?? const Duration(seconds: 4), + )); + } catch (e) { + debugPrint(e.toString()); + } + } +} + +// num +extension NumExtensions on num { + BorderRadius toRadius() { + return BorderRadius.circular(toDouble().r); + } + + RoundedRectangleBorder toRoundedRectRadius() { + return RoundedRectangleBorder(borderRadius: toRadius()); + } + + num percentOf(num value) { + return value * (this / 100.0); + } + + double toRadian() { + return this * pi / 180; + } + + double toDegree() { + return this * 180 / pi; + } + + double get milesToMeters => this * 1609.344; + + double get metersToMiles => this / 1609.344; +} + +// string +extension StringExtensions on String? { + bool isNullOrEmpty() { + return (this == null || this!.isEmpty); + } + + bool isNullOrEmptyNot() { + return (this != null && this!.isNotEmpty); + } + + String removeSpaces() { + return this?.replaceAll(RegExp(r"\s\b|\b\s"), "") ?? ""; + } + + bool get hasDigitsOnly { + return isNotNullOrEmpty() && RegExp(r'^[0-9]+$').hasMatch(this!); + } +} + +// widgets +extension WidgetsExtension on Widget { + // add click + Widget addInkwell({required Function onClick}) { + return InkWell( + child: this, + onTap: () => onClick(), + ); + } + + // add padding + Widget addPaddingAll(num value) { + return Padding( + padding: REdgeInsets.all(value.toDouble()), + child: this, + ); + } + + // add horizontal padding + Widget addPaddingHorizontal(num value) { + return Padding( + padding: REdgeInsets.symmetric(horizontal: value.toDouble()), + child: this, + ); + } + + // add vertical padding + Widget addPaddingVertical(num value) { + return Padding( + padding: REdgeInsets.symmetric(vertical: value.toDouble()), + child: this, + ); + } + + Widget alignCenterLeft() { + return Align(alignment: Alignment.centerLeft, child: this); + } + + Widget alignCenterRight() { + return Align(alignment: Alignment.centerRight, child: this); + } + + Widget alignCenter() { + return Align(alignment: Alignment.center, child: this); + } +} + +//bool +extension BoolExtensions on bool { + bool get not => !this; +} + +//object +extension ObjectExtension on Object? { + bool isNullObject() => this == null; + + bool isNotNull() => isNullObject().not; + + bool isNullOrEmpty() { + if (this is String) { + return (isNullObject() || (this as String).isEmpty); + } else if (this is List) { + return (isNullObject() || (this as List).isEmpty); + } else { + return isNullObject(); + } + } + + bool isNotNullOrEmpty() => isNullOrEmpty().not; +} + +extension HexColor on Color { + /// String is in the format "aabbcc" or "ffaabbcc" with an optional leading "#". + static Color fromHex(String hexString) { + final buffer = StringBuffer(); + if (hexString.length == 6 || hexString.length == 7) buffer.write('ff'); + buffer.write(hexString.replaceFirst('#', '')); + return Color(int.parse(buffer.toString(), radix: 16)); + } + + /// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`). + String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}' + '${alpha.toRadixString(16).padLeft(2, '0')}' + '${red.toRadixString(16).padLeft(2, '0')}' + '${green.toRadixString(16).padLeft(2, '0')}' + '${blue.toRadixString(16).padLeft(2, '0')}'; +} + +extension TimeOfDayChecker on TimeOfDay { + bool isBefore(TimeOfDay other) { + // print("now: $hour:$minute"); + // print("other: ${other.hour}:${other.minute}"); + // final int minutes1 = hour * 60 + minute; + // final int minutes2 = other.hour * 60 + other.minute; + // + // return minutes1 < minutes2; + + print("isBefore"); + print("now: $hour:$minute"); + print("other: ${other.hour}:${other.minute}"); + final n = DateTime.now(); + final d1 = DateTime(n.year, n.month, n.day, hour, minute); + final d2 = DateTime(n.year, n.month, n.day, other.hour, other.minute); + // final int minutes1 = hour * 60 + minute; + // final int minutes2 = other.hour * 60 + other.minute; + + return d1.isBefore(d2); + } + + bool isAfter(TimeOfDay other) { + print("isAfter"); + print("now: $hour:$minute"); + print("other: ${other.hour}:${other.minute}"); + final n = DateTime.now(); + final d1 = DateTime(n.year, n.month, n.day, hour, minute); + final d2 = DateTime(n.year, n.month, n.day, other.hour, other.minute); + // final int minutes1 = hour * 60 + minute; + // final int minutes2 = other.hour * 60 + other.minute; + + return d1.isAfter(d2); + } +} + +extension FutureExt on Future { + Future showLoader() { + return Get.showOverlay( + asyncFunction: () async => this, + opacity: 1, + opacityColor: Colors.black.withOpacity(0.5), + loadingWidget: const LoadingWidget(), + ); + } +} diff --git a/lib/utilities/extensions/export_extensions.dart b/lib/utilities/extensions/export_extensions.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/utilities/fcm_notifications.dart b/lib/utilities/fcm_notifications.dart new file mode 100644 index 0000000..04e8006 --- /dev/null +++ b/lib/utilities/fcm_notifications.dart @@ -0,0 +1,150 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'dart:io'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/foundation.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_app_badger/flutter_app_badger.dart'; +import 'notification_util.dart'; +import 'package:get/get.dart'; +import 'extensions/custom_extensions.dart'; + +class FcmNotification { + static FcmNotification? _instance; + + static FcmNotification getInstance() { + _instance ??= FcmNotification(); + return _instance!; + } + + init() async { + FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler); + await Firebase.initializeApp(); + _firebaseCloudMessagingListeners(); + } + + Future _firebaseCloudMessagingListeners() async { + // ios permission + if (Platform.isIOS) iOSPermission(); + await FirebaseMessaging.instance + .setForegroundNotificationPresentationOptions( + alert: false, // Required to display a heads up notification + badge: true, + sound: true, + ); + //foreground message + FirebaseMessaging.onMessage.listen((RemoteMessage remoteMessage) async { + // RemoteNotification notificationData = remoteMessage.notification; + + if (kDebugMode) log("new message received: ${remoteMessage.data}"); + + incrementNotificationCount(); + + // final data = Notifications.fromJson(remoteMessage.data); + + // if (Get.currentRoute == AppRoute.individualChatScreen && + // data.contentType == notificationContentTypeMessage) return; + + if (Platform.isAndroid) { + NotificationUtils.showDefaultNotification( + title: "${remoteMessage.notification?.title}", + body: '${remoteMessage.notification?.body}', + payload: remoteMessage.data, + ); + } + }); + // Also handle any interaction when the app is in the background via a + // Stream listener + FirebaseMessaging.onMessageOpenedApp.listen( + (RemoteMessage? remoteMessage) async { + if (remoteMessage != null) { + handleAppNotification(remoteMessage.data); + } + }, + ); + } + + Future iOSPermission() async { + NotificationSettings settings = + await FirebaseMessaging.instance.requestPermission( + alert: true, + announcement: false, + badge: true, + carPlay: false, + criticalAlert: false, + provisional: false, + sound: true, + ); + debugPrint('User granted permission: ${settings.authorizationStatus}'); + } + + void selectNotification(String? payload) async { + debugPrint('selected notification payload: $payload'); + + if (payload != null) { + var messageData = jsonDecode(payload); + handleAppNotification(messageData); + } + } +} + +handleAppNotification(Map payload) { + if (kDebugMode) { + log("Handle App Notification: $payload"); + } + // onNotificationTap(Notifications.fromJson(payload)); +} + +// onNotificationTap(Notifications notification) { +// if (TradePersonNotificationContentTypes.toOpenJobDetailPage +// .contains(notification.contentType)) { +// _gotoJobDetailScreen(notification.content); +// } else if (ClientNotificationContentTypes.toOpenJobDetailPage +// .contains(notification.contentType)) { +// if (notification.content == null) { +// debugPrint("content null"); +// return; +// } +// +// final id = JobDetail.fromJson(notification.content!).id; +// if (id.isNullOrEmpty()) { +// debugPrint("job id missing"); +// return; +// } +// AppRoute.toJobDetailScreen(args: JobDetailScreenArgs(jobId: id)); +// } else if (notification.contentType == notificationContentTypeMessage) { +// if (notification.content == null) { +// debugPrint("content null"); +// return; +// } +// final message = ChatModel.fromJson(notification.content); +// AppRoute.toIndividualChatScreen( +// arguments: IndividualChatScreenArgs( +// otherUserId: message.sentBy!.id!, +// name: message.sentBy!.fullname ?? emptyString, +// profilePicPath: message.sentBy!.profilePic ?? emptyString, +// otherUserType: message.sentBy!.userType!, +// onLastMessageUpdate: (ChatModel message) {}, +// ), +// ); +// } +// } + +incrementNotificationCount() async { + //Todo + // try { + // final appProviderController = AppProviderController.instance; + // appProviderController.notiCountUp(); + // if (await FlutterAppBadger.isAppBadgeSupported()) { + // FlutterAppBadger.updateBadgeCount( + // appProviderController.notificationCount()); + // } + // } catch (e) { + // debugPrint(e.toString()); + // } +} + +Future firebaseMessagingBackgroundHandler(RemoteMessage message) async { + debugPrint("Handling a background message: ${message.data}, ${message.ttl}"); + incrementNotificationCount(); +} diff --git a/lib/utilities/frequent_functions.dart b/lib/utilities/frequent_functions.dart new file mode 100644 index 0000000..31aeca3 --- /dev/null +++ b/lib/utilities/frequent_functions.dart @@ -0,0 +1,221 @@ +import 'package:get_time_ago/get_time_ago.dart'; +import 'extensions/custom_extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:intl/intl.dart'; +import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; + +class FrequentFunctions { + FrequentFunctions._(); + + static final _instance = FrequentFunctions._(); + // static Rx userModel = UserModel.empty().obs; + // static ProfileDataModel profileDataModelNew = ProfileDataModel.fromJson( + // json.decode(LocalStorageManager.getSessionToken( + // tokenKey: LocalStorageKeys.kProfileModelKey))); + + factory FrequentFunctions() { + return _instance; + } + + static const noWidget = SizedBox.shrink(); + + static const waterDropHeader = WaterDropHeader( + waterDropColor: CustomAppColors.kSecondaryColor, + complete: FrequentFunctions.noWidget, + failed: FrequentFunctions.noWidget, + completeDuration: Duration.zero, + ); + + static DateFormat careNoteDateFormatter = DateFormat("dd/MM/yyyy, hh:mm aa"); + + void logoutButtonPressed(BuildContext context) { + // LocalStorageManager.removeSession(token: LocalStorageKeys.kUserModelKey); + // LocalStorageManager.removeSession(token: LocalStorageKeys.kProfileModelKey); + LocalStorageManager.clear(); + + Navigator.pushNamedAndRemoveUntil( + context, CustomRouteNames.kLoginScreenRoute, (route) => false); + } + + static void showToast( + {required String message, Toast toast = Toast.LENGTH_SHORT}) { + Fluttertoast.showToast( + msg: message, + toastLength: toast, + gravity: ToastGravity.BOTTOM, + timeInSecForIosWeb: 1, + backgroundColor: CustomAppColors.kBlackColor, + textColor: CustomAppColors.kPrimaryColor, + fontSize: 13.0, + ); + } + + // static void showDialog( + // {required BuildContext context, + // required String title, + // required String description, + // required DialogType type, + // Color btnOkColor = CustomAppColors.kSecondaryColor, + // Function? onOkBtnPressed}) { + // AwesomeDialog( + // customHeader: Image.asset(AssetsManager.kAppIcon), + // dismissOnBackKeyPress: true, + // context: context, + // dialogType: type, + // headerAnimationLoop: false, + // animType: AnimType.scale, + // btnOkColor: btnOkColor, + // title: title, + // dismissOnTouchOutside: true, + // desc: description, + // btnOkOnPress: () { + // if (onOkBtnPressed != null) { + // onOkBtnPressed(); + // } + // }, + // ).show(); + // } + + List findDaysWithData(MonthWiseRecord monthWiseRecord) { + // print('Month: ${monthWiseRecord.id}'); + List rotaShiftListReturning = []; + + for (var weekRecord in [ + monthWiseRecord.week1, + monthWiseRecord.week2, + monthWiseRecord.week3, + monthWiseRecord.week4, + ]) { + for (var day in [ + weekRecord.mondayRecord, + weekRecord.tuesdayRecord, + weekRecord.wednesdayRecord, + weekRecord.thursdayRecord, + weekRecord.fridayRecord, + weekRecord.saturdayRecord, + weekRecord.sundayRecord + ]) { + if (day.rotaShift.havingShift && + day.rotaShift.shiftTime.isAfter(DateTime.now())) { + rotaShiftListReturning.add(day.rotaShift); + } + } + } + return rotaShiftListReturning; + } + + static Future datePicker(BuildContext context) async { + return await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime.now(), + lastDate: DateTime.now().add(const Duration(days: 365)), + ); + } + + static Future selectTime(BuildContext context, + {required TimeOfDay selectedTime, required Color themeColor}) => + showTimePicker( + context: context, + initialTime: selectedTime, + initialEntryMode: TimePickerEntryMode.dialOnly, + confirmText: "CONFIRM", + helpText: "Select Time", + builder: (context, child) { + return MediaQuery( + data: + MediaQuery.of(context).copyWith(alwaysUse24HourFormat: false), + child: Theme( + data: Theme.of(context).copyWith( + timePickerTheme: TimePickerThemeData( + backgroundColor: Colors.white, + // hourMinuteShape: const RoundedRectangleBorder( + // borderRadius: BorderRadius.all(Radius.circular(8)), + // side: BorderSide(color: Colors.orange, width: 4), + // ), + dayPeriodBorderSide: + const BorderSide(color: Colors.black, width: 1), + dayPeriodColor: MaterialStateColor.resolveWith((states) => + states.contains(MaterialState.selected) + ? themeColor + : CustomAppColors.kSmokeColor), + // shape: const RoundedRectangleBorder( + // borderRadius: BorderRadius.all(Radius.circular(8)), + // side: BorderSide(color: Colors.orange, width: 4), + // ), + dayPeriodTextColor: Colors.black, + // dayPeriodTextStyle: + // textStyle12w500.copyWith(fontWeight: FontWeight.bold), + // dayPeriodShape: const RoundedRectangleBorder( + // borderRadius: BorderRadius.all(Radius.circular(8)), + // side: BorderSide(color: Colors.orange, width: 4), + // ), + hourMinuteColor: MaterialStateColor.resolveWith((states) => + states.contains(MaterialState.selected) + ? themeColor + : CustomAppColors.kSmokeColor), + hourMinuteTextColor: MaterialStateColor.resolveWith( + (states) => states.contains(MaterialState.selected) + ? Colors.black + : Colors.black), + // hourMinuteTextStyle: const TextStyle( + // fontSize: 32, fontWeight: FontWeight.bold), + + dialHandColor: themeColor, + dialBackgroundColor: CustomAppColors.kSmokeColor, + dialTextColor: MaterialStateColor.resolveWith((states) => + states.contains(MaterialState.selected) + ? Colors.black + : Colors.black), + + // helpTextStyle: textStyle14w600, + inputDecorationTheme: const InputDecorationTheme( + border: InputBorder.none, + contentPadding: EdgeInsets.all(0), + ), + + entryModeIconColor: themeColor, + ), + textButtonTheme: TextButtonThemeData( + style: ButtonStyle( + // backgroundColor: MaterialStateColor.resolveWith((states) => Colors.orange), + foregroundColor: MaterialStateColor.resolveWith( + (states) => Colors.black), + // overlayColor: MaterialStateColor.resolveWith((states) => Colors.deepOrange), + ), + ), + ), + child: child!, + ), + ); + }); + + //eg: a = ["A", "B", "C"], b = ["1", "2"] + // zip(a, b) => ["A", "1", "B", "2", "C"] + static Iterable zip(Iterable a, Iterable b) sync* { + final ita = a.iterator; + final itb = b.iterator; + bool hasa, hasb; + while ((hasa = ita.moveNext()) | (hasb = itb.moveNext())) { + if (hasa) yield ita.current; + if (hasb) yield itb.current; + } + } + + static Widget centerText({required String text}) { + return Center( + child: CustomTextWidget( + text: text, + fontSize: 14.sp, + fontWeight: FontWeight.w600, + )); + } + + static String toTimesAgo(String? dateTime) { + return (dateTime.isNullOrEmpty()) + ? "" + : GetTimeAgo.parse(DateTime.parse(dateTime!).toLocal()); + } +} diff --git a/lib/utilities/image_picker_popup.dart b/lib/utilities/image_picker_popup.dart new file mode 100644 index 0000000..51858b8 --- /dev/null +++ b/lib/utilities/image_picker_popup.dart @@ -0,0 +1,216 @@ +import 'dart:io'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class ImagePickerPopup { + ImagePickerPopup._(); + + static final _picker = ImagePicker(); + + static void showImagePickerDialog( + // BuildContext context, + Function(File? file) onFetchImage, { + double? maxWidth, + double? maxHeight, + }) { + _checkCameraPermission().then((permissionGranted) { + debugPrint("permission status: $permissionGranted"); + if (permissionGranted) { + _showImageUploadBottomSheet( + onFetchImage, + maxWidth: maxWidth, + maxHeight: maxHeight, + ); + } else { + ScaffoldMessenger.of(Get.context!).showSnackBar( + SnackBar( + content: const Text( + 'Please allow permission to access Camera and photos.', + // style: textStyle16w400.copyWith(color: Colors.white), + ), + duration: const Duration(seconds: 5), + backgroundColor: Colors.black, + action: SnackBarAction( + label: 'Setting', onPressed: () => openAppSettings()), + ), + ); + } + }); + } + + static Future _checkCameraPermission() async { + // 1. Checking Initial Permission Status: + const cameraPermission = Permission.camera; + + final cameraPermissionStatus = await cameraPermission.status; + + if (cameraPermissionStatus.isGranted) { + debugPrint("status: true"); + return true; + } else { + Map permissionsResult = await [cameraPermission].request(); + if (permissionsResult.values.isNotEmpty && + permissionsResult[cameraPermission] == PermissionStatus.granted) { + debugPrint("status2: true"); + return true; + } else { + debugPrint("status3: false"); + return await cameraPermission.shouldShowRequestRationale; + } + } + } + + static void _showImageUploadBottomSheet( + // BuildContext context, + Function(File) onFetchImage, { + double? maxWidth, + double? maxHeight, + }) { + Get.bottomSheet( + Container( + padding: const EdgeInsets.all(20), + child: ListView( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: [ + const Text( + "Upload Image", + // style: textStyle16w700.copyWith(color: Colors.black), + textAlign: TextAlign.center, + ), + const SizedBox(height: 5), + const Text( + "Click a Photo or upload your image from saved photos.", + // style: textStyle12w500.copyWith(color: Colors.black), + textAlign: TextAlign.center, + ), + const SizedBox(height: 20), + Row( + children: [ + const SizedBox(width: 30), + Expanded( + child: InkWell( + onTap: () { + Get.back(); + // Navigator.of(context).pop(); + getImageFromSource( + fromCamera: true, + onFetchImage: onFetchImage, + maxWidth: maxWidth, + maxHeight: maxHeight, + ); + }, + borderRadius: + const BorderRadius.all(Radius.circular(20.0)), + child: Container( + padding: const EdgeInsets.all(10.0), + child: Column( + children: [ + Icon( + CupertinoIcons.camera, + size: 50.r, + color: Get.theme.primaryColor, + ), + const SizedBox(height: 8), + const Text( + "Camera", + // style: textStyle16w700.copyWith( + // color: Colors.black) + ), + ], + ), + ), + ), + ), + SizedBox( + width: 40.r, + height: 64.r, + child: const Center( + child: VerticalDivider(color: Colors.grey, width: 2), + ), + ), + Expanded( + child: InkWell( + onTap: () { + // Navigator.of(context).pop(); + Get.back(); + getImageFromSource( + fromCamera: false, onFetchImage: onFetchImage); + }, + borderRadius: + const BorderRadius.all(Radius.circular(20.0)), + child: Container( + padding: const EdgeInsets.all(10.0), + child: Column( + children: [ + // SvgPicture.asset( + // Assets.svgPlaceholderImg2, + // width: 50, + // height: 50, + // ), + Icon( + CupertinoIcons.photo, + size: 50.r, + color: Get.theme.primaryColor, + ), + const SizedBox(height: 8), + const Text( + "Gallery", + // style: textStyle16w700.copyWith( + // color: Colors.black) + ), + ], + ), + ), + ), + ), + const SizedBox( + width: 30, + ), + ], + ) + ], + ), + ), + backgroundColor: Colors.white); + } + + static void getImageFromSource({ + required bool fromCamera, + required Function(File) onFetchImage, + double? maxWidth, + double? maxHeight, + }) async { + final permissionGranted = await _checkCameraPermission(); + debugPrint("permission status: $permissionGranted"); + + if (permissionGranted) { + final pickedFile = await _picker.pickImage( + source: fromCamera ? ImageSource.camera : ImageSource.gallery, + imageQuality: 80, + maxWidth: maxWidth, + maxHeight: maxHeight, + ); + if (pickedFile != null) { + final picture = File(pickedFile.path); + onFetchImage(picture); + } + } else { + ScaffoldMessenger.of(Get.context!).showSnackBar( + SnackBar( + content: const Text( + 'Please allow permission to access Camera and photos.', + ), + duration: const Duration(seconds: 5), + backgroundColor: Colors.black, + action: SnackBarAction( + label: 'Setting', onPressed: () => openAppSettings()), + ), + ); + } + } +} diff --git a/lib/utilities/local_storage_manager/export_local_storage.dart b/lib/utilities/local_storage_manager/export_local_storage.dart new file mode 100644 index 0000000..aeafe71 --- /dev/null +++ b/lib/utilities/local_storage_manager/export_local_storage.dart @@ -0,0 +1,2 @@ +export 'local_storage_keys.dart'; +export 'local_storage_manager.dart'; \ No newline at end of file diff --git a/lib/utilities/local_storage_manager/local_storage_keys.dart b/lib/utilities/local_storage_manager/local_storage_keys.dart new file mode 100644 index 0000000..95a8256 --- /dev/null +++ b/lib/utilities/local_storage_manager/local_storage_keys.dart @@ -0,0 +1,13 @@ +class LocalStorageKeys { + LocalStorageKeys._(); + + static const String kUserTokenKey = "UserTokenKey"; + static const String kUserIdKey = "UserIdKey"; + // static const String kRememberMeKey = "RememberMeKey"; + static const String kSaveEmailKey = "SaveEmailKey"; + // static const String kUserModelKey = "UserModelKey"; + // static const String kProfileModelKey = "ProfileModelKey"; + // static const String kIsUserLoggedInKey = "IsUserLoggedInKey "; + + static const String kCurrentOngoingShift = "CurrentOngoingShift "; +} \ No newline at end of file diff --git a/lib/utilities/local_storage_manager/local_storage_manager.dart b/lib/utilities/local_storage_manager/local_storage_manager.dart new file mode 100644 index 0000000..ae46fec --- /dev/null +++ b/lib/utilities/local_storage_manager/local_storage_manager.dart @@ -0,0 +1,73 @@ +import 'package:flutter/foundation.dart'; +import 'package:ftc_mobile_app/models/rota/WeekArrayData.dart'; +import 'package:get_storage/get_storage.dart'; + +import 'local_storage_keys.dart'; + +abstract class LocalStorageManager { + // LocalStorageManager._(); + // + // static final LocalStorageManager _instance = LocalStorageManager._(); + // + // factory LocalStorageManager() { + // return _instance; + // } + + static final GetStorage _box = GetStorage(); + + static Future init() => GetStorage.init(); + + static Future saveSession({ + required String tokenKey, + required String tokenValue, + }) async { + await _box.write(tokenKey, tokenValue); + } + + static Future removeSession({required String token}) async { + await _box.remove(token); + } + + static String getSessionToken({required String tokenKey}) { + return _box.read(tokenKey) ?? ''; + } + + static Future setLoginToken(String token) async { + await _box.write(LocalStorageKeys.kUserTokenKey, token); + } + + static String getLoginToken() { + return _box.read(LocalStorageKeys.kUserTokenKey) ?? ''; + } + + static Future setUserId(String id) async { + await _box.write(LocalStorageKeys.kUserIdKey, id); + } + + static String get userId { + return _box.read(LocalStorageKeys.kUserIdKey) ?? ''; + } + + static void saveShiftData({required DaysArrayData data}) { + _box.write(LocalStorageKeys.kCurrentOngoingShift, data.toJson()); + } + + static DaysArrayData? getOngoingShift() { + try { + return DaysArrayData.fromJson( + _box.read(LocalStorageKeys.kCurrentOngoingShift)); + } catch (e) { + debugPrint( + "LocalStorageManager.getCurrentShiftData err: ${e.toString()}"); + return null; + } + } + + static Future removeOngoingShift() async { + await _box.remove(LocalStorageKeys.kCurrentOngoingShift); + } + + static Future clear() { + return _box.erase(); + } +} diff --git a/lib/utilities/mixins/export_mixins.dart b/lib/utilities/mixins/export_mixins.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/utilities/notification_util.dart b/lib/utilities/notification_util.dart new file mode 100644 index 0000000..fe319c1 --- /dev/null +++ b/lib/utilities/notification_util.dart @@ -0,0 +1,183 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'fcm_notifications.dart'; +import 'extensions/custom_extensions.dart'; + +const String packageName = "com.inajam.app"; +const notificationChannelId = packageName; +const notificationChannelName = "inajam"; +const notificationChannelDescription = "In A Jam"; + +final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = + FlutterLocalNotificationsPlugin(); + +//for SDK version 33 +Future requestNotificationPermissions() async { + if (Platform.isIOS || Platform.isMacOS) { + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + IOSFlutterLocalNotificationsPlugin>() + ?.requestPermissions( + alert: true, + badge: true, + sound: true, + critical: true, + ); + } else if (Platform.isAndroid) { + final AndroidFlutterLocalNotificationsPlugin? androidImplementation = + flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>(); + + await androidImplementation?.requestNotificationsPermission(); + } +} + +const AndroidInitializationSettings initializationSettingsAndroid = + AndroidInitializationSettings('@drawable/notification_icon'); + +const DarwinInitializationSettings initializationSettingsDarwin = + DarwinInitializationSettings( + // requestAlertPermission: true, + // requestBadgePermission: true, + // requestSoundPermission: true, + // onDidReceiveLocalNotification: + // (int id, String? title, String? body, String? payload) async { + // didReceiveLocalNotificationStream.add( + // ReceivedNotification( + // id: id, + // title: title, + // body: body, + // payload: payload, + // ), + // ); + // }, + // notificationCategories: darwinNotificationCategories, + ); + +const InitializationSettings initializationSettings = InitializationSettings( + android: initializationSettingsAndroid, + iOS: initializationSettingsDarwin, + macOS: initializationSettingsDarwin, +); + +void selectNotification(String? payload) async { + debugPrint('selected notification payload: $payload'); + debugPrint( + 'is selected notification payload null or empty: ${payload.isNotNullOrEmpty()}'); + + if (payload.isNotNullOrEmpty()) { + var messageData = jsonDecode(payload!); + handleAppNotification(messageData); + } +} + +//----------------------- +//----------------------- +//----------------------- +class NotificationUtils { + NotificationUtils._(); + + static NotificationDetails? platformChannelSpecifics; + static AndroidNotificationChannel? channel; + static AndroidNotificationDetails? androidPlatformChannelSpecifics; + + static Future init() async { + try { + await flutterLocalNotificationsPlugin.initialize(initializationSettings, + onDidReceiveNotificationResponse: (response) => + selectNotification(response.payload) + + // onDidReceiveNotificationResponse: + // (NotificationResponse notificationResponse) { + // switch (notificationResponse.notificationResponseType) { + // case NotificationResponseType.selectedNotification: + // selectNotificationStream.add(notificationResponse.payload); + // break; + // case NotificationResponseType.selectedNotificationAction: + // if (notificationResponse.actionId == navigationActionId) { + // selectNotificationStream.add(notificationResponse.payload); + // } + // break; + // } + // }, + // onDidReceiveBackgroundNotificationResponse: notificationTapBackground, + ); + + platformChannelSpecifics = await getChannelSpecifics(); + } catch (e) { + debugPrint("NotificationUtils: ${e.toString()}"); + } + } + + static Future getChannelSpecifics( + [int? badgeNumber]) async { + if (Platform.isAndroid) { + channel = await _getAndroidDefaultChannel(notificationChannelId, + notificationChannelName, notificationChannelDescription); + } + androidPlatformChannelSpecifics ??= AndroidNotificationDetails( + notificationChannelId, notificationChannelName, + channelDescription: notificationChannelDescription, + importance: channel?.importance ?? Importance.max, + priority: Priority.high, + enableVibration: true, + playSound: false); + + int badgeNum = 0; + + //Todo + // try { + // badgeNum = + // badgeNumber ?? AppProviderController.instance.notificationCount(); + // print("badgeNum: $badgeNum"); + // } catch (e) {} + + var iOSPlatformChannelSpecifics = DarwinNotificationDetails( + presentSound: true, + presentBadge: true, + presentAlert: true, + badgeNumber: badgeNum, + ); + return NotificationDetails( + android: androidPlatformChannelSpecifics, + iOS: iOSPlatformChannelSpecifics, + ); + } + + static Future showDefaultNotification( + {required String title, + required String body, + Map? payload, + int? badgeNumber}) async { + platformChannelSpecifics = await getChannelSpecifics(badgeNumber); + await flutterLocalNotificationsPlugin.show( + 10, + title, + body, + platformChannelSpecifics, + payload: (payload.isNullOrEmpty()) ? null : jsonEncode(payload!), + ); + } + + static Future _getAndroidDefaultChannel( + String channelId, + String channelName, + String? channelDescription, + ) async { + AndroidNotificationChannel channel = AndroidNotificationChannel( + channelId, + channelName, + description: channelDescription, + importance: Importance.max, + enableVibration: true, + playSound: false, + ); + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.createNotificationChannel(channel); + return channel; + } +} diff --git a/lib/view/custom_widgets/auth/custom_forget_password_dialog.dart b/lib/view/custom_widgets/auth/custom_forget_password_dialog.dart new file mode 100644 index 0000000..15363ca --- /dev/null +++ b/lib/view/custom_widgets/auth/custom_forget_password_dialog.dart @@ -0,0 +1,165 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; + +class CustomForgetPasswordDialog extends StatelessWidget { + const CustomForgetPasswordDialog({ + Key? key, + required this.dialogMessageText, + required this.headingText, + required this.dialogMessageTextBold, + required this.dialogButtonAcceptText, + required this.dialogButtonCloseText, + // this.acceptFunction, + }) : super(key: key); + + final String headingText; + final String dialogMessageText; + final String dialogMessageTextBold; + final String dialogButtonAcceptText; + + // final Function? acceptFunction; + final String dialogButtonCloseText; + + @override + Widget build(BuildContext context) { + TextEditingController emailController = TextEditingController(); + RxString emailErrorMsg = "".obs; + + bool validateEmail() { + if (emailController.text.isEmpty) { + emailErrorMsg.value = ConstantText.kEmailIsRequired; + } else if (!GetUtils.isEmail(emailController.text)) { + emailErrorMsg.value = ConstantText.kInvalidEmail; + } else { + emailErrorMsg.value = ""; + } + return emailErrorMsg.isEmpty; + } + + return AlertDialog( + surfaceTintColor: CustomAppColors.kPrimaryColor, + shape: LinearBorder.top(side: BorderSide.none), + backgroundColor: CustomAppColors.kWhiteColor, + shadowColor: CustomAppColors.kWhiteColor, + titlePadding: EdgeInsets.only(top: 20.h), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CustomTextWidget( + text: headingText, + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + ), + Container( + padding: EdgeInsets.all(10.sp), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTextWidget( + text: dialogMessageText, + isExpanded: false, + fontSize: 10.sp, + fontWeight: FontWeight.w400, + textAlign: TextAlign.center, + ), + SizedBox( + height: 3.h, + ), + dialogMessageTextBold != "" + ? CustomTextWidget( + text: dialogMessageTextBold, + isExpanded: false, + fontSize: 10.sp, + fontWeight: FontWeight.bold, + textAlign: TextAlign.left, + ) + : Container(), + ], + ), + ), + // SizedBox(height: 10.h,), + Padding( + padding: EdgeInsets.only(top: 5.0.h), + child: CustomTextFieldWidget( + controller: emailController, + hintText: ConstantText.kPleaseInputEmail, + heading: ConstantText.kEmailHeading, + onChange: (_) { + validateEmail(); + }, + ), + ), + Obx(() { + return CustomErrorMsg( + message: emailErrorMsg.value, + ); + }), + 16.verticalSpace, + Row( + children: [ + Expanded( + child: SizedBox( + height: 32.h, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + minimumSize: Size(double.infinity, 30.h), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + backgroundColor: CustomAppColors.kSecondaryColor), + onPressed: () async { + if (validateEmail()) { + Navigator.of(context).pop(); + var response = await AuthService() + .forgetPassword(email: emailController.text) + .showLoader(); + if (response == true) { + FrequentFunctions.showToast( + message: + "A password reset link has been sent to your registered email.", + ); + } + // Add functionality for the "Agree" button. + } + }, + child: CustomTextWidget( + text: dialogButtonAcceptText, + fontColor: CustomAppColors.kPrimaryColor, + fontSize: 14.sp, + fontWeight: FontWeight.w400), + ), + ), + ), + SizedBox(width: 10.h), + Expanded( + child: SizedBox( + height: 32.h, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + minimumSize: Size(double.infinity, 30.h), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + backgroundColor: CustomAppColors.kLightGreyColor), + onPressed: () { + // Add functionality for the "pop" button. + Navigator.of(context).pop(); + }, + child: CustomTextWidget( + text: dialogButtonCloseText, + fontColor: CustomAppColors.kPrimaryColor, + fontSize: 14.sp, + fontWeight: FontWeight.w400), + ), + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/view/custom_widgets/auth/export_auth_widgets.dart b/lib/view/custom_widgets/auth/export_auth_widgets.dart new file mode 100644 index 0000000..7077229 --- /dev/null +++ b/lib/view/custom_widgets/auth/export_auth_widgets.dart @@ -0,0 +1 @@ +export 'custom_forget_password_dialog.dart'; \ No newline at end of file diff --git a/lib/view/custom_widgets/clients/CareNoteOptionCard.dart b/lib/view/custom_widgets/clients/CareNoteOptionCard.dart new file mode 100644 index 0000000..41eceb2 --- /dev/null +++ b/lib/view/custom_widgets/clients/CareNoteOptionCard.dart @@ -0,0 +1,46 @@ +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:flutter/material.dart'; + +class CareNoteOptionCard extends StatelessWidget { + final String icon; + final String name; + + const CareNoteOptionCard({ + super.key, + required this.icon, + required this.name, + }); + + @override + Widget build(BuildContext context) { + return Card( + elevation: 2, + shadowColor: CustomAppColors.kLightGreyColor, + surfaceTintColor: Colors.white, + color: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4).r), + child: Padding( + padding: REdgeInsets.all(8), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CustomImageWidget( + imagePath: icon, + height: 24.r, + width: 24.r, + ), + 12.verticalSpace, + CustomTextWidget( + text: name, + alignment: Alignment.center, + isExpanded: false, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kDarkBlueTextColor, + fontSize: 14.sp) + ], + ), + ), + ); + } +} diff --git a/lib/view/custom_widgets/clients/category_subcategory_dropdowns_widget.dart b/lib/view/custom_widgets/clients/category_subcategory_dropdowns_widget.dart new file mode 100644 index 0000000..065a6f4 --- /dev/null +++ b/lib/view/custom_widgets/clients/category_subcategory_dropdowns_widget.dart @@ -0,0 +1,170 @@ +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/category_subcategory_widget_controller.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/body_points_category.dart'; +import 'package:get/get.dart'; + +class CategorySubcategoryDropdownsWidget extends StatelessWidget { + final CategorySubcategoryWidgetController controller; + + const CategorySubcategoryDropdownsWidget( + {super.key, required this.controller}); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _bodyPartsDropdown, + _subcategoryDropdown, + ], + ); + } + + Widget get _bodyPartsDropdown { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.r), + border: Border.all( + color: CustomAppColors.kLightGreyColor, + width: 1.sp, + ), + ), + padding: EdgeInsets.symmetric( + vertical: 5.h, + horizontal: 15.w, + ), + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTextWidget( + text: "Select Category", + fontSize: 10.sp, + fontWeight: FontWeight.w500, + fontColor: CustomAppColors.kLightTextColor, + alignment: Alignment.centerLeft, + ), + Obx(() { + return DropdownButtonHideUnderline( + child: DropdownButtonFormField( + onTap: () { + FocusScopeNode().unfocus(); + }, + dropdownColor: Colors.white, + decoration: const InputDecoration( + border: InputBorder.none, + ), + hint: Text( + "Select...", + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 14.sp, + color: CustomAppColors.kLightTextColor, + ), + ), + items: controller.bodyPointsCategoryList + .map( + (e) => DropdownMenuItem( + value: e, + child: Text(e.name), + ), + ) + .toList(), + isExpanded: true, + iconSize: 20.h, + icon: Padding( + padding: REdgeInsets.only(right: 4.0), + child: const Icon(Icons.arrow_drop_down_sharp, + color: Colors.grey), + ), + onChanged: (category) { + controller.selectedBodyPart.value = category; + + if ((category?.subCategory ?? []).isEmpty) { + controller.selectedSubcategory.value = null; + } + }, + ), + ); + }), + ], + ), + ); + } + + Widget get _subcategoryDropdown { + return Obx(() { + return (controller.selectedBodyPart.value?.subCategory ?? []).isEmpty + ? const SizedBox.shrink() + : Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.r), + border: Border.all( + color: CustomAppColors.kLightGreyColor, + width: 1.sp, + ), + ), + margin: REdgeInsets.only(top: 20.r), + padding: EdgeInsets.symmetric(vertical: 5.h, horizontal: 15.w), + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTextWidget( + text: "Select Subcategory", + fontSize: 10.sp, + fontWeight: FontWeight.w500, + fontColor: CustomAppColors.kLightTextColor, + alignment: Alignment.centerLeft, + ), + DropdownButtonHideUnderline( + child: DropdownButtonFormField( + onTap: () { + FocusScopeNode().unfocus(); + }, + dropdownColor: Colors.white, + decoration: const InputDecoration( + border: InputBorder.none, + ), + hint: Text( + "Select...", + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 14.sp, + color: CustomAppColors.kLightTextColor, + ), + ), + items: controller.selectedBodyPart.value!.subCategory + .map( + (e) => DropdownMenuItem( + value: e, + child: Text(e.name), + ), + ) + .toList(), + isExpanded: true, + iconSize: 20.h, + icon: Padding( + padding: REdgeInsets.only(right: 4.0), + child: const Icon(Icons.arrow_drop_down_sharp, + color: Colors.grey), + ), + onChanged: (value) { + if (value != null) { + controller.selectedSubcategory.value = value; + } + }, + ), + ), + ], + ), + ); + }); + } +} diff --git a/lib/view/custom_widgets/clients/custom_icon_tile.dart b/lib/view/custom_widgets/clients/custom_icon_tile.dart new file mode 100644 index 0000000..4978c4c --- /dev/null +++ b/lib/view/custom_widgets/clients/custom_icon_tile.dart @@ -0,0 +1,56 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomIconTile extends StatelessWidget { + const CustomIconTile({ + super.key, + required this.iconPath, + required this.text, + required this.route, + this.arguments, + }); + + final String iconPath; + final String text; + final String route; + final Object? arguments; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () { + if (route.isNotEmpty) { + Navigator.pushNamed(context, route, arguments: arguments); + } + }, + child: Container( + padding: REdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(2.r), + border: Border.all(color: CustomAppColors.kSecondaryColor)), + child: Row( + children: [ + CustomImageWidget( + imagePath: iconPath, + height: 16.r, + width: 16.r, + ), + 8.horizontalSpace, + Expanded( + child: CustomTextWidget( + text: text, + alignment: Alignment.centerLeft, + isExpanded: false, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kDarkBlueTextColor, + fontSize: 14.sp), + ), + Icon(Icons.arrow_forward_ios_rounded, + size: 12.r, color: CustomAppColors.kSecondaryColor) + ], + ), + ), + ); + } +} diff --git a/lib/view/custom_widgets/clients/show_documents_option_dialog.dart b/lib/view/custom_widgets/clients/show_documents_option_dialog.dart new file mode 100644 index 0000000..313f238 --- /dev/null +++ b/lib/view/custom_widgets/clients/show_documents_option_dialog.dart @@ -0,0 +1,161 @@ +// +// +// import 'package:flutter/material.dart'; +// import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +// import 'package:ftc_mobile_app/models/clients/service_users_model.dart'; +// import 'package:get/get.dart'; +// import '../../../models/clients/documents_list_model.dart'; +// +// class ShowDocumentsOptionDialog extends StatelessWidget { +// final DocumentModel documentModel; +// +// const ShowDocumentsOptionDialog({ +// super.key,required this.documentModel, +// }); +// +// @override +// Widget build(BuildContext context) { +// final DocumentsListScreenController controller = Get.find(); +// return AlertDialog( +// surfaceTintColor: CustomAppColors.kPrimaryColor, +// shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.r)), +// backgroundColor: CustomAppColors.kPrimaryColor, +// title: Center( +// child: CustomTextWidget( +// text: 'Please Select what you wanna do', +// fontWeight: FontWeight.bold, +// isExpanded: false, +// alignment: Alignment.center, +// fontSize: 16.sp, +// ), +// ), +// content: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// mainAxisSize: MainAxisSize.min, +// children: [ +// Row( +// mainAxisAlignment: MainAxisAlignment.spaceAround, +// children: [ +// InkWell( +// onTap: () async{ +// var response = await Navigator.pushNamed(controller.screenKey.currentContext!, +// CustomRouteNames.kAddNewDocumentScreenRoute, +// arguments: [documentModel,controller.serviceUser.value]); +// if(response is List){ +// int index = controller.documentsList.value.documentList.indexWhere((item) => item == documentModel); +// controller.documentsList.value.documentList[index].title = response[0]; +// controller.documentsList.value.documentList[index].details = response[1]; +// controller.documentsList.refresh(); +// } +// }, +// child: Container( +// padding: EdgeInsets.all(1.sp), +// decoration: BoxDecoration( +// color: CustomAppColors.kYellowColor.withOpacity(0.5), +// borderRadius: BorderRadius.circular(50.r), +// ), +// child: const Icon(Icons.edit,color: CustomAppColors.kDarkYellowColor,size: 40,)), +// ), +// SizedBox(width: 3.w,), +// InkWell( +// onTap: () async { +// dynamic response = await ClientService().deleteDocumentService(docId: documentModel.id); +// if (response is bool) { +// controller.removeDocumentItem(documentModel); +// Navigator.pop(controller.screenKey.currentState!.context); +// } +// }, +// child: Container( +// padding: EdgeInsets.all(1.sp), +// decoration: BoxDecoration( +// color: CustomAppColors.kRedColor.withOpacity(0.5), +// borderRadius: BorderRadius.circular(50.r), +// ), +// child: const Icon(Icons.delete,color: CustomAppColors.kRedColor,size: 40,)), +// ), +// SizedBox(width: 3.w,), +// InkWell( +// onTap: () async{ +// dynamic response = await Navigator.pushNamed( +// controller.screenKey.currentContext!, +// CustomRouteNames.kAddNewDocumentScreenRoute, +// arguments: controller.serviceUser.value +// ); +// if(response is DocumentModel){ +// controller.documentsList.value.documentList.insert(0,response); +// controller.documentsList.refresh(); +// } +// }, +// child: Container( +// padding: EdgeInsets.all(1.sp), +// decoration: BoxDecoration( +// color: CustomAppColors.kSecondaryColor.withOpacity(0.5), +// borderRadius: BorderRadius.circular(50.r), +// ), +// child: const Icon(Icons.add,color: CustomAppColors.kSecondaryColor,size: 40,)), +// ), +// SizedBox(width: 3.w,), +// InkWell( +// onTap: () { +// Navigator.pushNamed( +// controller.screenKey.currentContext!, +// CustomRouteNames.kAddNewDocumentScreenRoute, +// arguments: [documentModel,true] +// ); +// }, +// child: Container( +// padding: EdgeInsets.all(1.sp), +// decoration: BoxDecoration( +// color: CustomAppColors.kGreenColor.withOpacity(0.5), +// borderRadius: BorderRadius.circular(50.r), +// ), +// child: const Icon(Icons.visibility,color: CustomAppColors.kGreenColor,size: 40,)), +// ), +// ], +// ), +// ], +// ), +// actions: [ +// Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// mainAxisAlignment: MainAxisAlignment.start, +// children: [ +// Visibility( +// visible: false, +// child: ElevatedButton( +// style: ElevatedButton.styleFrom( +// minimumSize: Size(double.infinity, 30.h), +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.circular(5.r), +// ), +// backgroundColor: CustomAppColors.kSecondaryColor +// ), +// +// onPressed: () { +// // Add functionality for the "Cancel Shift" button. +// Navigator.of(context).pop(); +// }, +// child: const CustomTextWidget(text: 'Cancel Shift',fontColor: CustomAppColors.kPrimaryColor), +// ), +// ), +// ElevatedButton( +// style: ElevatedButton.styleFrom( +// minimumSize: Size(double.infinity, 30.h), +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.circular(5.r), +// ), +// backgroundColor: CustomAppColors.kPrimaryColor +// ), +// +// onPressed: () { +// // Add functionality for the "Close" button. +// Navigator.of(context).pop(); +// }, +// child: CustomTextWidget(text: 'Close',fontColor: CustomAppColors.kBlackColor,fontSize: 15.sp), +// ), +// ], +// ), +// ], +// ); +// } +// } \ No newline at end of file diff --git a/lib/view/custom_widgets/common_cancel_button.dart b/lib/view/custom_widgets/common_cancel_button.dart new file mode 100644 index 0000000..df2ae78 --- /dev/null +++ b/lib/view/custom_widgets/common_cancel_button.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CommonCloseTextButton extends StatelessWidget { + final VoidCallback? onTap; + + const CommonCloseTextButton({super.key, this.onTap}); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap ?? () => Navigator.of(context).pop(), + child: Container( + height: 30.h, + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kLightGreyColor), + borderRadius: BorderRadius.circular(5.r), + ), + alignment: Alignment.center, + child: CustomTextWidget( + text: 'Close', + fontColor: CustomAppColors.kBlackColor, + fontSize: 14.sp, + fontWeight: FontWeight.w400, + ), + ), + ); + } +} diff --git a/lib/view/custom_widgets/custom_app_bar.dart b/lib/view/custom_widgets/custom_app_bar.dart new file mode 100644 index 0000000..a86f3cc --- /dev/null +++ b/lib/view/custom_widgets/custom_app_bar.dart @@ -0,0 +1,143 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { + final VoidCallback? onBackButtonPressed; + final Widget? action; + final Widget? leadingButton; + final Widget? titleWidget; + final String titleText; + final bool showBoxShadow; + + const CustomAppBar({ + Key? key, + this.onBackButtonPressed, + this.showBoxShadow = true, + this.leadingButton, + this.action, + this.titleWidget, + this.titleText = "", + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final ModalRoute? parentRoute = ModalRoute.of(context); + final bool canPop = parentRoute?.canPop ?? false; + return PreferredSize( + preferredSize: preferredSize, + child: Container( + decoration: BoxDecoration( + color: CustomAppColors.kPrimaryColor, + boxShadow: showBoxShadow + ? [ + BoxShadow( + color: CustomAppColors.kLightGreyColor, + spreadRadius: 1.2, + blurRadius: 4, + offset: const Offset(0, 2), + ), + ] + : null, + ), + width: MediaQuery.of(context).size.width, + alignment: Alignment.center, + padding: EdgeInsets.only( + top: GetPlatform.isIOS ? 40.0 : 20.0, + ), + child: Stack( + children: [ + Container( + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.only(top: 4.sp, left: 20.0.sp, right: 20.sp), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Builder(builder: (context) { + if (titleWidget != null) { + return titleWidget!; + } + return FittedBox( + child: CustomTextWidget( + text: titleText, + fontColor: CustomAppColors.kIconColor, + fontSize: 22.0.sp, + fontWeight: FontWeight.w500, + ), + ); + }), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Visibility( + visible: canPop || leadingButton != null, + child: Padding( + padding: EdgeInsets.only( + left: 10.0.sp, + top: 32.0.sp, + ), + child: leadingButton != null + ? leadingButton! + : GestureDetector( + onTap: () { + if (onBackButtonPressed != null) { + onBackButtonPressed!(); + } else { + Navigator.pop(context); + } + }, + child: SizedBox( + width: 30.0, + height: 30.0, + child: Container( + padding: Platform.isIOS + ? const EdgeInsets.symmetric( + horizontal: 10.0, + vertical: 5.0, + ) + : null, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4.sp), + color: CustomAppColors.kLightGreyColor, + ), + alignment: Alignment.center, + child: Icon( + Icons.adaptive.arrow_back, + color: CustomAppColors.kIconColor, + size: 20.0, + ), + ), + ), + ), + ), + ), + const Spacer(), + Visibility( + visible: action != null, + child: Padding( + padding: EdgeInsets.only( + right: 15.0, + top: 25.0.sp, + ), + child: action, + ), + ), + ], + ), + ], + ), + ), + ); + } + + @override + Size get preferredSize => const Size.fromHeight(56.0); +} diff --git a/lib/view/custom_widgets/custom_app_bar_title_only.dart b/lib/view/custom_widgets/custom_app_bar_title_only.dart new file mode 100644 index 0000000..3023d35 --- /dev/null +++ b/lib/view/custom_widgets/custom_app_bar_title_only.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomAppBarTitleOnly extends AppBar { + CustomAppBarTitleOnly(BuildContext context, + {super.key, required String titleText}) + : super( + toolbarHeight: 56.r, + leading: IconButton( + icon: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + onPressed: () { + Navigator.pop(context); + }, + ), + centerTitle: false, + titleSpacing: 0, + leadingWidth: 50.r, + surfaceTintColor: Colors.white, + title: CustomTextWidget( + text: titleText, + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + maxLines: 1, + textOverflow: TextOverflow.ellipsis, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ); +} diff --git a/lib/view/custom_widgets/custom_app_bar_with_action.dart b/lib/view/custom_widgets/custom_app_bar_with_action.dart new file mode 100644 index 0000000..8572e15 --- /dev/null +++ b/lib/view/custom_widgets/custom_app_bar_with_action.dart @@ -0,0 +1,187 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomAppBarWithAction extends AppBar { + CustomAppBarWithAction( + BuildContext context, { + super.key, + required String titleText, + required String actionText, + Color? actionTextColor, + required Function() onActionTap, + }) : super( + toolbarHeight: 56.r, + leading: IconButton( + icon: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + onPressed: () { + Navigator.pop(context); + }, + ), + centerTitle: false, + titleSpacing: 0, + leadingWidth: 50.r, + surfaceTintColor: Colors.white, + title: CustomTextWidget( + text: titleText, + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + maxLines: 1, + textOverflow: TextOverflow.ellipsis, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + actions: [ + TextButton( + onPressed: onActionTap, + child: Text( + actionText, + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w600, + color: actionTextColor ?? CustomAppColors.kLightTextColor, + ), + ), + ) + ], + ); +} + +// class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { +// final VoidCallback? onBackButtonPressed; +// final String titleText; +// final bool showBoxShadow; +// +// const CustomAppBar({ +// Key? key, +// this.onBackButtonPressed, +// this.showBoxShadow = true, +// this.titleText = "", +// }) : super(key: key); +// +// @override +// Widget build(BuildContext context) { +// final ModalRoute? parentRoute = ModalRoute.of(context); +// final bool canPop = parentRoute?.canPop ?? false; +// return PreferredSize( +// preferredSize: preferredSize, +// child: Container( +// decoration: BoxDecoration( +// color: CustomAppColors.kPrimaryColor, +// boxShadow: showBoxShadow +// ? [ +// BoxShadow( +// color: CustomAppColors.kLightGreyColor, +// spreadRadius: 1.2, +// blurRadius: 4, +// offset: const Offset(0, 2), +// ), +// ] +// : null, +// ), +// width: MediaQuery.of(context).size.width, +// alignment: Alignment.center, +// padding: EdgeInsets.only( +// top: GetPlatform.isIOS ? 40.0 : 20.0, +// ), +// child: Stack( +// children: [ +// Container( +// width: MediaQuery.of(context).size.width, +// padding: EdgeInsets.only(top: 4.sp, left: 20.0.sp, right: 20.sp), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.center, +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// Builder(builder: (context) { +// CustomTextWidget( +// text: titleText, +// isExpanded: false, +// fontSize: 16.sp, +// fontWeight: FontWeight.w700, +// fontColor: CustomAppColors.kDarkBlueTextColor, +// ); +// return FittedBox( +// child: CustomTextWidget( +// text: titleText, +// fontColor: CustomAppColors.kIconColor, +// fontSize: 22.0.sp, +// fontWeight: FontWeight.w500, +// ), +// ); +// }), +// ], +// ), +// ), +// Row( +// mainAxisAlignment: MainAxisAlignment.center, +// crossAxisAlignment: CrossAxisAlignment.center, +// mainAxisSize: MainAxisSize.min, +// children: [ +// Visibility( +// visible: canPop || leadingButton != null, +// child: Padding( +// padding: EdgeInsets.only( +// left: 10.0.sp, +// top: 32.0.sp, +// ), +// child: leadingButton != null +// ? leadingButton! +// : GestureDetector( +// onTap: () { +// if (onBackButtonPressed != null) { +// onBackButtonPressed!(); +// } else { +// Navigator.pop(context); +// } +// }, +// child: SizedBox( +// width: 30.0, +// height: 30.0, +// child: Container( +// padding: Platform.isIOS +// ? const EdgeInsets.symmetric( +// horizontal: 10.0, +// vertical: 5.0, +// ) +// : null, +// decoration: BoxDecoration( +// borderRadius: BorderRadius.circular(4.sp), +// color: CustomAppColors.kLightGreyColor, +// ), +// alignment: Alignment.center, +// child: Icon( +// Icons.adaptive.arrow_back, +// color: CustomAppColors.kIconColor, +// size: 20.0, +// ), +// ), +// ), +// ), +// ), +// ), +// const Spacer(), +// Visibility( +// visible: action != null, +// child: Padding( +// padding: EdgeInsets.only( +// right: 15.0, +// top: 25.0.sp, +// ), +// child: action, +// ), +// ), +// ], +// ), +// ], +// ), +// ), +// ); +// } +// +// @override +// Size get preferredSize => const Size.fromHeight(56.0); +// } diff --git a/lib/view/custom_widgets/custom_app_button.dart b/lib/view/custom_widgets/custom_app_button.dart new file mode 100644 index 0000000..c7a9818 --- /dev/null +++ b/lib/view/custom_widgets/custom_app_button.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomAppButton extends StatelessWidget { + final String buttonText; + final VoidCallback? onTap; + final Color? buttonColor; + final Color? textColor; + final Color? borderColor; + final bool isLoading; + + const CustomAppButton({ + super.key, + this.buttonColor, + this.textColor, + required this.buttonText, + this.onTap, + this.isLoading = false, + this.borderColor, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + if(isLoading) { + return; + } + FocusScope.of(context).unfocus(); + if(onTap!=null) { + onTap!(); + } + }, + child: Container( + height: 48.h, + decoration: BoxDecoration( + color: buttonColor ?? CustomAppColors.kSecondaryColor, + borderRadius: BorderRadius.circular(3.r), + border: Border.all( + color: borderColor ?? CustomAppColors.kSecondaryColor, + ), + ), + // padding: EdgeInsets.symmetric(vertical: 15.h,), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CustomTextWidget( + text: buttonText, + fontWeight: FontWeight.w600, + isExpanded: false, + fontSize: 16.sp, + fontColor: textColor ?? CustomAppColors.kPrimaryColor, + ), + + Visibility( + visible: isLoading, + child: Padding( + padding: EdgeInsets.only(left: 8.0.w), + child: SizedBox( + height: 15.h, + width: 15.w, + child: CircularProgressIndicator( + color: textColor ?? CustomAppColors.kPrimaryColor, + strokeWidth: 3.0.w, + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/view/custom_widgets/custom_check_box.dart b/lib/view/custom_widgets/custom_check_box.dart new file mode 100644 index 0000000..20536ee --- /dev/null +++ b/lib/view/custom_widgets/custom_check_box.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomCheckBox extends StatelessWidget { + final bool checkBoxValue; + final String titleText; + final VoidCallback? onTap; + + const CustomCheckBox({ + super.key, + required this.checkBoxValue, + required this.titleText, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 25.h, + width: 25.w, + child: Checkbox( + value: checkBoxValue, + activeColor: CustomAppColors.kSecondaryColor, + onChanged: (_) { + if(onTap!=null) { + onTap!(); + } + }, + ), + ), + Padding( + padding: EdgeInsets.only(left: 8.0.w), + child: GestureDetector( + onTap: onTap, + child: CustomTextWidget( + text: titleText, + isExpanded: false, + fontWeight: FontWeight.w500, + fontSize: 12.sp, + ), + ), + ), + ], + ); + } +} diff --git a/lib/view/custom_widgets/custom_error_msg.dart b/lib/view/custom_widgets/custom_error_msg.dart new file mode 100644 index 0000000..d07006f --- /dev/null +++ b/lib/view/custom_widgets/custom_error_msg.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomErrorMsg extends StatelessWidget { + final String message; + final Alignment? alignment; + + const CustomErrorMsg({ + super.key, + required this.message, + this.alignment, + }); + + @override + Widget build(BuildContext context) { + return Visibility( + visible: message.isNotEmpty, + child: CustomTextWidget( + text: message, + fontColor: CustomAppColors.kRedColor, + fontWeight: FontWeight.w500, + alignment: alignment ?? Alignment.centerRight, + ), + ); + } +} diff --git a/lib/view/custom_widgets/custom_image_widgets.dart b/lib/view/custom_widgets/custom_image_widgets.dart new file mode 100644 index 0000000..433b2ac --- /dev/null +++ b/lib/view/custom_widgets/custom_image_widgets.dart @@ -0,0 +1,104 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomImageWidget extends StatelessWidget { + final String imagePath; + final BoxFit? fit; + final double? width; + final double? height; + final Color? imageColor; + + const CustomImageWidget({ + Key? key, + this.imagePath = "", + this.fit, + this.width, + this.height, + this.imageColor, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + if (imagePath.startsWith("http")) { + // handel network image here. + return Image.network( + imagePath, + fit: fit, + height: height, + width: width, + color: imageColor, + errorBuilder: (ctx, obj, stc) { + return _ErrorWidget( + size: height ?? 50.0, + color: imageColor, + ); + }, + ); + } else if (imagePath.trim().isEmpty) { + return _ErrorWidget( + size: height ?? 50.0, + color: imageColor, + ); + } else if (imagePath.startsWith("assets/") && imagePath.endsWith(".png")) { + return Image.asset( + imagePath, + fit: fit, + height: height, + width: width, + color: imageColor, + errorBuilder: (ctx, obj, stc) { + return _ErrorWidget( + size: height ?? 50.0, + color: imageColor, + ); + }, + ); + } else if(imagePath.startsWith("assets/") && imagePath.endsWith(".svg")) { + return SvgPicture.asset( + imagePath, + height: height, + width: width, + colorFilter: imageColor!=null? ColorFilter.mode(imageColor!, BlendMode.srcIn):null, + // color: imageColor, + ); + } + return Image.file( + File(imagePath), + fit: fit, + height: height, + width: width, + errorBuilder: (ctx, obj, stc) { + return _ErrorWidget( + size: height ?? 50.0, + color: imageColor, + ); + }, + ); + } +} + +class _ErrorWidget extends StatelessWidget { + final double size; + final Color? color; + + const _ErrorWidget({ + Key? key, + this.size = 50.0, + this.color, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.center, + child: Icon( + Icons.error_outline, + size: size, + color: color ?? CustomAppColors.kIconColor, + ), + ); + } +} diff --git a/lib/view/custom_widgets/custom_navigation_drawer.dart b/lib/view/custom_widgets/custom_navigation_drawer.dart new file mode 100644 index 0000000..038d7be --- /dev/null +++ b/lib/view/custom_widgets/custom_navigation_drawer.dart @@ -0,0 +1,268 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/view/screens/webview/webview_screen.dart'; +import 'package:get/get.dart'; +import 'my_circle_image.dart'; + +class CustomDrawer extends StatefulWidget { + const CustomDrawer({Key? key}) : super(key: key); + + @override + State createState() => _CustomDrawerState(); +} + +class _CustomDrawerState extends State { + DashboardScreenController dashboardController = + Get.put(DashboardScreenController()); + CustomNavigationDrawerController drawerController = + Get.put(CustomNavigationDrawerController()); + + @override + Widget build(BuildContext context) { + return Drawer( + elevation: 5, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(1.r)), + backgroundColor: CustomAppColors.kPrimaryColor, + surfaceTintColor: CustomAppColors.kPrimaryColor, + child: SafeArea( + child: Column( + children: [ + 40.verticalSpace, + SizedBox( + width: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Center( + child: GestureDetector( + onTap: () { + drawerController.selectedIndex.value = -1; + Navigator.pop(context); + Navigator.pushNamed( + context, CustomRouteNames.kViewProfileScreenRoute); + }, + child: Obx(() { + return MyCircleImage( + imageSize: 80.r, + url: + "${WebUrls.baseUrl}${DashboardScreenController.instance.myProfileData()?.user?.profilePictureUrl ?? ""}", + errorWidget: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + imageColor: CustomAppColors.kDarkBlueTextColor, + height: 80.r, + width: 80.r, + ), + ); + }), + ), + ), + 10.verticalSpace, + Obx( + () => CustomTextWidget( + text: DashboardScreenController.instance + .myProfileData() + ?.staffMemberName ?? + "", + textAlign: TextAlign.center, + fontColor: CustomAppColors.kBlackColor, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + ), + ), + Obx( + () => CustomTextWidget( + text: DashboardScreenController.instance + .myProfileData() + ?.staffDesignation ?? + "", + fontColor: CustomAppColors.kLightGreyColor, + fontSize: 10.sp, + fontWeight: FontWeight.w500), + ), + ], + ), + ), + Expanded( + child: Container( + width: double.infinity, + padding: EdgeInsets.only(top: 20.h, bottom: 20.h), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + _getDrawerNavItem( + title: "Home", + iconPath: AssetsManager.kHomeIcon, + color: CustomAppColors.kLightGreyColor, + selected: drawerController.selectedIndex.value == 1, + onTap: () { + Navigator.pop(context); + dashboardController.selectedIndex.value = 1; + drawerController.selectedIndex.value = 1; + // Navigator.pushNamed( + // context, + // CustomRouteNames.kDashboardScreenRoute, + // ); + }), + 6.verticalSpace, + _getDrawerNavItem( + title: "Rota", + iconPath: AssetsManager.kPersonMainIcon, + color: CustomAppColors.kLightGreyColor, + selected: drawerController.selectedIndex.value == 0, + onTap: () { + Navigator.pop(context); + // dashboardController.selectedIndex.value = 0; + // drawerController.selectedIndex.value = 0; + Navigator.pushNamed( + context, + CustomRouteNames.kRotaDashboardScreenRoute, + ); + }), + 6.verticalSpace, + _getDrawerNavItem( + title: "Clients", + iconPath: AssetsManager.kPeopleIcon, + color: CustomAppColors.kLightGreyColor, + selected: drawerController.selectedIndex.value == 3, + onTap: () { + Navigator.pop(context); + dashboardController.selectedIndex.value = 3; + drawerController.selectedIndex.value = 3; + // Navigator.pushNamed( + // context, + // CustomRouteNames.kClientsListScreenRoute, + // ); + }), + 6.verticalSpace, + _getDrawerNavItem( + title: "Inbox", + iconPath: AssetsManager.kMessageIcon, + color: CustomAppColors.kLightGreyColor, + selected: drawerController.selectedIndex.value == 2, + onTap: () { + Navigator.pop(context); + dashboardController.selectedIndex.value = 2; + drawerController.selectedIndex.value = 2; + // Navigator.pushNamed( + // context, + // CustomRouteNames.kInboxScreenRoute, + // ); + }), + 6.verticalSpace, + _getDrawerNavItem( + title: "Policies and Procedures", + iconPath: AssetsManager.kPoliciesIcon, + color: CustomAppColors.kLightGreyColor, + onTap: () { + Navigator.pop(context); + Navigator.pushNamed( + context, + CustomRouteNames.kWebviewScreen, + arguments: WebviewScreenArgument( + title: 'Policies and Procedures', + url: ConstantText.privacyUrl), + ); + }), + 6.verticalSpace, + _getDrawerNavItem( + title: "Settings", + iconPath: AssetsManager.kSettingsIcon, + color: CustomAppColors.kLightGreyColor, + onTap: () { + Navigator.pop(context); + //Todo: uncomment when start working + // Navigator.pushNamed( + // context, + // CustomRouteNames.kSettingsScreen, + // ); + }), + 6.verticalSpace, + _getDrawerNavItem( + title: "Notifications", + iconPath: AssetsManager.kBellIcon, + color: CustomAppColors.kLightGreyColor, + selected: drawerController.selectedIndex.value == 6, + onTap: () { + Navigator.pop(context); + //Todo: uncomment + // Navigator.pushNamed( + // context, + // CustomRouteNames.kNotificationListScreenRoute, + // ); + }), + const Expanded(child: FrequentFunctions.noWidget), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CustomTextWidget( + text: "Shift end at ", + fontColor: CustomAppColors.kLightGreyColor, + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + ), + CustomTextWidget( + text: "07Hrs 50Sec", + fontColor: CustomAppColors.kSecondaryColor, + isExpanded: false, + fontWeight: FontWeight.w700, + fontSize: 16.sp, + ), + ], + ) + ], + ), + ), + ), + ], + ), + ), + ); + } + + Widget _getDrawerNavItem( + {required String title, + required String iconPath, + Color color = CustomAppColors.kSecondaryColor, + bool selected = false, + required Function() onTap}) { + return Container( + color: selected + ? CustomAppColors.kSecondaryColor + : CustomAppColors.kPrimaryColor, + padding: REdgeInsets.symmetric(horizontal: 26, vertical: 12), + child: GestureDetector( + onTap: onTap, + // onTap: ()=> controller.onTap(title), + child: Row( + children: [ + // Icon( + // icon, + // size: 27, + // color: selected ? CustomAppColors.kWhiteColor : color, + // ), + CustomImageWidget( + imagePath: iconPath, + width: title == "Clients" ? 20.w : 22.w, + height: title == "Clients" ? 20.h : 26.h, + // height: 26.h, + imageColor: selected ? CustomAppColors.kWhiteColor : color, + ), + 10.horizontalSpace, + Expanded( + child: CustomTextWidget( + text: title, + textAlign: TextAlign.left, + fontColor: selected + ? CustomAppColors.kWhiteColor + : CustomAppColors.kIconColor, + fontSize: 18.sp, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/view/custom_widgets/custom_radio_button.dart b/lib/view/custom_widgets/custom_radio_button.dart new file mode 100644 index 0000000..7094670 --- /dev/null +++ b/lib/view/custom_widgets/custom_radio_button.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:ftc_mobile_app/utilities/custom_app_colors.dart'; +import 'package:get/get.dart'; +import 'custom_text_widget.dart'; + +class RadioButton extends StatelessWidget { + final String value; + final Rx selectedOption; + final bool isEnabled; + + const RadioButton({ + super.key, + required this.value, + required this.selectedOption, + this.isEnabled = true, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () { + selectedOption(value); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Ink( + width: 32.r, + height: 32.r, + child: Obx(() { + return Radio( + value: value, + groupValue: selectedOption(), + onChanged: selectedOption, + fillColor: MaterialStateProperty.resolveWith((states) { + if (!isEnabled) return CustomAppColors.kLightGreyColor; + + if (states.contains(MaterialState.selected)) { + return CustomAppColors.kSecondaryColor; + } + return Colors.black; + }), + ); + }), + ), + CustomTextWidget( + text: value, + isExpanded: false, + fontSize: 14.sp, + fontWeight: FontWeight.w400, + fontColor: + isEnabled ? Colors.black : CustomAppColors.kLightGreyColor, + ) + ], + ), + ); + } +} diff --git a/lib/view/custom_widgets/custom_scaffold.dart b/lib/view/custom_widgets/custom_scaffold.dart new file mode 100644 index 0000000..4b47007 --- /dev/null +++ b/lib/view/custom_widgets/custom_scaffold.dart @@ -0,0 +1,166 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomScaffold extends StatelessWidget { + final VoidCallback? onBackButton; + final VoidCallback? onScreenTap; + final String? titleText; + final bool avoidBottomInsets; + final bool? showShadow; + final bool showAppBar; + final bool enableLayoutBuilder; + final GlobalKey screenKey; + final PreferredSizeWidget? appBar; + final Widget? body; + final Widget? sideDrawer; + final Widget? customTabViewWidget; + final Widget? bottomMenu; + final Widget? floatingActionButton; + final Color? backgroundColor; + + const CustomScaffold({ + Key? key, + this.onBackButton, + this.customTabViewWidget, + this.titleText, + this.appBar, + this.body, + this.showShadow, + this.bottomMenu, + this.floatingActionButton, + this.onScreenTap, + this.avoidBottomInsets = true, + this.showAppBar = false, + this.enableLayoutBuilder = true, + required this.screenKey, + this.backgroundColor, + this.sideDrawer, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final ModalRoute? parentRoute = ModalRoute.of(context); + final bool canPop = parentRoute?.canPop ?? false; + return GestureDetector( + onHorizontalDragEnd: GetPlatform.isIOS + ? (details) { + if (details.velocity.pixelsPerSecond.dx > 0) { + if (onBackButton != null) { + onBackButton!(); + } else { + if (canPop) { + Navigator.pop(context); + } + } + } + } + : null, + onTap: () { + if (onScreenTap != null) onScreenTap!(); + }, + child: WillPopScope( + onWillPop: () async { + if (onBackButton != null) { + onBackButton!(); + } else { + if (canPop) { + Navigator.pop(context); + } + } + return false; + }, + child: LayoutBuilder( + builder: (context, constraint) { + if (constraint.maxWidth > 600 && enableLayoutBuilder) { + return Scaffold( + drawer: sideDrawer, + key: screenKey, + resizeToAvoidBottomInset: avoidBottomInsets, + drawerEnableOpenDragGesture: false, + endDrawerEnableOpenDragGesture: false, + backgroundColor: backgroundColor ?? CustomAppColors.kPrimaryColor, + body: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Spacer(), + Expanded( + flex: 2, + child: customTabViewWidget ?? + Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + 2.0.sp, + ), + color: backgroundColor ?? + CustomAppColors.kPrimaryColor, + ), + padding: EdgeInsets.all( + 10.0.sp, + ), + child: body, + ), + ], + ), + ), + const Spacer(), + ], + ), + appBar: showAppBar + ? appBar ?? + CustomAppBar( + titleText: titleText ?? "titleText", + onBackButtonPressed: () { + if (onBackButton != null) { + onBackButton!(); + return; + } + if (canPop) { + Navigator.pop(context); + } + }, + ) + : null, + floatingActionButton: floatingActionButton, + ); + } + return Scaffold( + drawer: sideDrawer, + resizeToAvoidBottomInset: avoidBottomInsets, + key: screenKey, + floatingActionButton: floatingActionButton, + appBar: showAppBar + ? appBar ?? + CustomAppBar( + showBoxShadow: showShadow ?? false, + titleText: titleText ?? "titleText", + onBackButtonPressed: () { + if (onBackButton != null) { + onBackButton!(); + return; + } + if (canPop) { + Navigator.pop(context); + } + }, + ) + : null, + body: body ?? Container(), + backgroundColor: + backgroundColor ?? CustomAppColors.kPrimaryColor, + bottomNavigationBar: bottomMenu, + ); + }, + ), + ), + ); + } +} diff --git a/lib/view/custom_widgets/custom_text_field_widget.dart b/lib/view/custom_widgets/custom_text_field_widget.dart new file mode 100644 index 0000000..c29365c --- /dev/null +++ b/lib/view/custom_widgets/custom_text_field_widget.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomTextFieldWidget extends StatelessWidget { + final TextEditingController? controller; + final int? minLines; + final int? maxLines; + final int? maxLength; + final String hintText; + final String heading; + final ValueChanged? onChange; + final bool isEnabled; + final bool isObscure; + final BorderRadius? borderRadius; + final Color? borderColor; + final double? borderWidth; + final Widget? bottomChild; + final TextInputType? inputType; + final TextCapitalization? textCapitalization; + final List? inputFormatters; + + const CustomTextFieldWidget({ + super.key, + this.controller, + required this.heading, + this.minLines = 1, + this.maxLines = 1, + this.maxLength, + this.hintText = "", + this.onChange, + this.isEnabled = true, + this.isObscure = false, + this.borderRadius, + this.borderColor, + this.borderWidth = 1.0, + this.bottomChild, + this.textCapitalization, + this.inputType, + this.inputFormatters, + }); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: borderRadius, + border: Border.all( + color: borderColor ?? CustomAppColors.kSmokeColor, + width: borderWidth ?? 1.5.sp, + ), + ), + padding: EdgeInsets.symmetric( + vertical: 10.h, + horizontal: 15.w, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // CustomTextWidget( + // text: heading, + // fontSize: 10.sp, + // fontWeight: FontWeight.w500, + // fontColor: CustomAppColors.kLightTextColor, + // alignment: Alignment.centerLeft, + // textAlign: TextAlign.left, + // ), + Text( + heading, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 10.sp, + fontWeight: FontWeight.w500, + color: CustomAppColors.kLightTextColor, + ), + ), + 4.verticalSpace, + TextField( + controller: controller, + enabled: isEnabled, + obscureText: isObscure, + minLines: minLines, + maxLines: maxLines, + maxLength: maxLength, + inputFormatters: inputFormatters, + onChanged: (_) { + if (onChange != null) { + onChange!(_); + } + }, + keyboardType: inputType, + textCapitalization: textCapitalization ?? TextCapitalization.none, + cursorColor: CustomAppColors.kSecondaryColor, + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 14.sp, + color: CustomAppColors.kIconColor, + ), + decoration: InputDecoration( + isDense: true, + hintText: hintText, + hintStyle: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 14.sp, + color: CustomAppColors.kLightTextColor, + ), + counterText: "", + contentPadding: EdgeInsets.zero, + border: const OutlineInputBorder( + borderSide: BorderSide.none, + ), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide.none, + ), + disabledBorder: const OutlineInputBorder( + borderSide: BorderSide.none, + ), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide.none, + ), + ), + ), + if (bottomChild != null) bottomChild!, + ], + ), + ); + } +} diff --git a/lib/view/custom_widgets/custom_text_widget.dart b/lib/view/custom_widgets/custom_text_widget.dart new file mode 100644 index 0000000..856e320 --- /dev/null +++ b/lib/view/custom_widgets/custom_text_widget.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomTextWidget extends StatelessWidget { + final String text; + final double? fontSize; + final bool isExpanded; + final FontWeight? fontWeight; + final Color? fontColor; + final String? fontFamily; + final Alignment? alignment; + final TextOverflow? textOverflow; + final TextAlign textAlign; + final TextDecoration textDecoration; + final int? maxLines; + + const CustomTextWidget({ + Key? key, + required this.text, + this.fontSize, + this.fontWeight, + this.fontColor, + this.alignment, + this.isExpanded = true, + this.fontFamily, + this.textOverflow, + this.textAlign = TextAlign.center, + this.textDecoration = TextDecoration.none, + this.maxLines, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + alignment: alignment, + width: isExpanded ? MediaQuery.of(context).size.width : null, + child: Text( + text, + textAlign: textAlign, + maxLines: maxLines, + style: TextStyle( + fontSize: fontSize, + fontWeight: fontWeight, + color: fontColor ?? CustomAppColors.kIconColor, + fontFamily: fontFamily, + decoration: textDecoration, + decorationColor: fontColor ?? CustomAppColors.kIconColor, + ), + overflow: textOverflow, + ), + ); + } +} \ No newline at end of file diff --git a/lib/view/custom_widgets/edit_icon.dart b/lib/view/custom_widgets/edit_icon.dart new file mode 100644 index 0000000..1a37785 --- /dev/null +++ b/lib/view/custom_widgets/edit_icon.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:ftc_mobile_app/utilities/custom_app_colors.dart'; + +class EditIcon extends StatelessWidget { + final VoidCallback onTap; + + const EditIcon({super.key, required this.onTap}); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Container( + width: 24.r, + height: 24.r, + // padding: REdgeInsets.all(8), + decoration: BoxDecoration( + color: CustomAppColors.kYellowColor.withOpacity(0.5), + borderRadius: BorderRadius.circular(50.r), + ), + child: Center( + child: Icon( + Icons.edit, + color: CustomAppColors.kDarkYellowColor, + size: 16.r, + ), + )), + ); + } +} diff --git a/lib/view/custom_widgets/export_custom_widgets.dart b/lib/view/custom_widgets/export_custom_widgets.dart new file mode 100644 index 0000000..5793967 --- /dev/null +++ b/lib/view/custom_widgets/export_custom_widgets.dart @@ -0,0 +1,16 @@ +export 'custom_text_widget.dart'; +export 'custom_app_bar.dart'; +export 'custom_scaffold.dart'; +export 'custom_image_widgets.dart'; +export 'custom_text_field_widget.dart'; +export 'custom_error_msg.dart'; +export 'custom_check_box.dart'; +export 'custom_app_button.dart'; +export 'rota/export_rota.dart'; +export 'home/export_home_widgets.dart'; +export 'notifications/export_notification_widgets.dart'; +export 'custom_navigation_drawer.dart'; +export 'auth/export_auth_widgets.dart'; +export 'custom_radio_button.dart'; +export 'loading_widget.dart'; +export 'custom_app_bar_title_only.dart'; \ No newline at end of file diff --git a/lib/view/custom_widgets/home/custom_message_dialog.dart b/lib/view/custom_widgets/home/custom_message_dialog.dart new file mode 100644 index 0000000..bca0a23 --- /dev/null +++ b/lib/view/custom_widgets/home/custom_message_dialog.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class CustomMessageDialog extends StatelessWidget { + const CustomMessageDialog({Key? key, required this.dialogMessageText, required this.headingText, required this.dialogMessageTextBold, required this.dialogButtonText}) : super(key: key); + + final String headingText; + final String dialogMessageText; + final String dialogMessageTextBold; + final String dialogButtonText; + + @override + Widget build(BuildContext context) { + return AlertDialog( + surfaceTintColor: CustomAppColors.kPrimaryColor, + insetPadding: REdgeInsets.all(18), + contentPadding: REdgeInsets.all(15), + shape: LinearBorder.top(side: BorderSide.none), + backgroundColor: CustomAppColors.kWhiteColor, + shadowColor: CustomAppColors.kWhiteColor, + // titlePadding: EdgeInsets.only(top: 20.h), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CustomTextWidget( + text: headingText, + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + ), + 8.verticalSpace, + Container( + padding: EdgeInsets.all(10.sp), + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kLightGreyColor)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTextWidget( + text: dialogMessageText, + isExpanded: false, + fontSize: 10.sp, + fontWeight: FontWeight.w400, + textAlign: TextAlign.left, + ), + 12.verticalSpace, + dialogMessageTextBold != "" + ? CustomTextWidget( + text: dialogMessageTextBold, + isExpanded: false, + fontSize: 10.sp, + fontWeight: FontWeight.bold, + textAlign: TextAlign.left, + ) + : Container(), + ], + ), + ), + 12.verticalSpace, + SizedBox( + height: 32.h, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + minimumSize: Size(double.infinity, 30.h), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + backgroundColor: CustomAppColors.kSecondaryColor), + onPressed: () { + // Add functionality for the "Agree" button. + Navigator.of(context).pop(); + }, + child: CustomTextWidget( + text: dialogButtonText, + fontColor: CustomAppColors.kPrimaryColor, + fontSize: 14.sp, + fontWeight: FontWeight.w400), + ), + ), + ], + ), + ); + } +} diff --git a/lib/view/custom_widgets/home/custom_privacy_policy_dialog.dart b/lib/view/custom_widgets/home/custom_privacy_policy_dialog.dart new file mode 100644 index 0000000..6497ab7 --- /dev/null +++ b/lib/view/custom_widgets/home/custom_privacy_policy_dialog.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class PrivacyPolicyDialog extends StatelessWidget { + const PrivacyPolicyDialog({Key? key, required this.privacyPolicy, required this.checkBoxOnChange}) : super(key: key); + + final String privacyPolicy; + final ValueChanged? checkBoxOnChange; + + @override + Widget build(BuildContext context) { + return AlertDialog( + surfaceTintColor: CustomAppColors.kPrimaryColor, + insetPadding: EdgeInsets.only(right: 18.w,left: 18.w, top:40.h ,bottom: MediaQuery.of(context).size.height/3.h), + contentPadding: EdgeInsets.only(left: 15.w,right: 15.w,top: 11.h,bottom: 0.h), + shape: LinearBorder.top(side: BorderSide.none), + backgroundColor: CustomAppColors.kWhiteColor, + shadowColor: CustomAppColors.kWhiteColor, + titlePadding: EdgeInsets.only(top: 20.h), + title: CustomTextWidget( + text: "New Policy", + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + ), + content: Column( + children: [ + Container( + padding: EdgeInsets.all(10.sp), + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kLightGreyColor)), + child: CustomTextWidget( + text: privacyPolicy, + isExpanded: false, + fontSize: 10.sp, + fontWeight: FontWeight.w400, + textAlign: TextAlign.left, + ), + ), + const Spacer(), + SizedBox( + height: 30.h, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Checkbox( + activeColor: CustomAppColors.kSecondaryColor, + value: true, + onChanged: (value) { + if (checkBoxOnChange != null && value!= null) { + checkBoxOnChange!(value); + } + }), + CustomTextWidget( + text: "Agree to the new policy", + isExpanded: false, + fontSize: 10.sp, + fontWeight: FontWeight.w400, + ), + ], + ), + ), + Container( + // padding: EdgeInsets.only(bottom: 50.h), + // height: 25.h, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + minimumSize: Size(double.infinity, 30.h), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + backgroundColor: CustomAppColors.kSecondaryColor), + onPressed: () { + // Add functionality for the "Agree" button. + Navigator.of(context).pop(); + }, + child: CustomTextWidget( + text: 'Agree', + fontColor: CustomAppColors.kPrimaryColor, + fontSize: 14.sp, + fontWeight: FontWeight.w400), + ), + ), + ], + ), + ); + } +} diff --git a/lib/view/custom_widgets/home/export_home_widgets.dart b/lib/view/custom_widgets/home/export_home_widgets.dart new file mode 100644 index 0000000..2e999ef --- /dev/null +++ b/lib/view/custom_widgets/home/export_home_widgets.dart @@ -0,0 +1,2 @@ +export 'custom_privacy_policy_dialog.dart'; +export 'custom_message_dialog.dart'; \ No newline at end of file diff --git a/lib/view/custom_widgets/human_body_mapper_widget.dart b/lib/view/custom_widgets/human_body_mapper_widget.dart new file mode 100644 index 0000000..dc556f5 --- /dev/null +++ b/lib/view/custom_widgets/human_body_mapper_widget.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import '../../utilities/enums/body_parts.dart'; + +class HumanBodyWidget extends StatefulWidget { + final double width; + final Map visibleBodyPoints; + final Function(BodyPart, Offset position)? onPointTap; + + const HumanBodyWidget({ + super.key, + required this.visibleBodyPoints, + required this.width, + this.onPointTap, + }); + + @override + State createState() => _HumanBodyWidgetState(); +} + +class _HumanBodyWidgetState extends State { + final GlobalKey _imageKey = GlobalKey(); + + //Note: change the size if changing [AssetsManager.pngHumanBodyFrontBack] image + final _actualImageSize = const Size(500, 681); + + late double height; + + @override + void initState() { + super.initState(); + + height = widget.width / _actualImageSize.aspectRatio; + + WidgetsBinding.instance.addPostFrameCallback((_) { + Size? imageSize = _imageKey.currentContext?.size; + // Now you can use imageSize.width and imageSize.height + + print("image size: $imageSize"); + }); + } + + @override + Widget build(BuildContext context) { + return UnconstrainedBox( + child: SizedBox( + width: widget.width, + height: height, + child: LayoutBuilder(builder: (_, constrains) { + return Stack( + fit: StackFit.loose, + children: [ + Positioned.fill( + child: Image.asset( + AssetsManager.pngHumanBodyFrontBack, + key: _imageKey, + width: MediaQuery.of(context).size.width, + fit: BoxFit.contain, + ), + ), + ...widget.visibleBodyPoints.entries + .map((e) => _pointWidget( + bodyPart: e.key, + color: e.value, + constrains: constrains, + onPointTap: widget.onPointTap, + )) + .toList() + ], + ); + }), + ), + ); + } + + Positioned _pointWidget({ + required BodyPart bodyPart, + required BoxConstraints constrains, + Color? color, + Function(BodyPart, Offset position)? onPointTap, + }) { + return Positioned( + top: constrains.maxHeight * (bodyPart.topPercent / 100.0), + left: constrains.maxWidth * (bodyPart.leftPercent / 100.0), + child: InkWell( + onTap: () { + // RenderBox? button = context.findRenderObject() as RenderBox?; + // RenderBox? overlay = + // Overlay.of(context).context.findRenderObject() as RenderBox?; + // Rect? buttonRect = (button == null) + // ? null + // : Rect.fromPoints( + // button.localToGlobal(Offset.zero, ancestor: overlay), + // button.localToGlobal(button.size.bottomRight(Offset.zero), + // ancestor: overlay), + // ); + // double screenHeight = MediaQuery.of(context).size.height; + // double popupHeight = screenHeight - (buttonRect?.bottom ?? 0.5.sh); + }, + onTapUp: (d) { + if (onPointTap != null) { + onPointTap(bodyPart, d.globalPosition); + } + }, + child: Container( + width: 12.r, + height: 12.r, + decoration: BoxDecoration( + color: color ?? CustomAppColors.kRedColor, + border: Border.all(color: CustomAppColors.kWhiteColor, width: 2), + borderRadius: BorderRadius.circular(6.r), + ), + ), + ), + ); + } +} diff --git a/lib/view/custom_widgets/label_value_box_widget.dart b/lib/view/custom_widgets/label_value_box_widget.dart new file mode 100644 index 0000000..f10dd86 --- /dev/null +++ b/lib/view/custom_widgets/label_value_box_widget.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class LabelValueBoxWidget extends StatelessWidget { + const LabelValueBoxWidget({ + super.key, + required this.label, + required this.value, + this.trailing, + this.borderColor, + }); + + final String label; + final String value; + final Color? borderColor; + final Widget? trailing; + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.all(10.h), + decoration: BoxDecoration( + border: Border.all(color: borderColor ?? Colors.grey), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTextWidget( + text: label, + fontColor: CustomAppColors.kLightGreyColor, + fontWeight: FontWeight.w500, + isExpanded: false, + textAlign: TextAlign.left, + fontSize: 10.sp, + ), + 6.verticalSpace, + CustomTextWidget( + text: value, + fontColor: CustomAppColors.kBlackColor, + fontWeight: FontWeight.w600, + fontSize: 14.0.sp, + textAlign: TextAlign.left, + isExpanded: false, + ), + ], + ), + ), + trailing ?? const SizedBox.shrink() + ], + ), + ); + } +} + diff --git a/lib/view/custom_widgets/loading_widget.dart b/lib/view/custom_widgets/loading_widget.dart new file mode 100644 index 0000000..e0c1a4f --- /dev/null +++ b/lib/view/custom_widgets/loading_widget.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class LoadingWidget extends StatelessWidget { + const LoadingWidget({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Material( + type: MaterialType.transparency, + child: Center( + child: Container( + width: 150.w, + height: 170.h, + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(8.r)), + child: LoaderColumn( + loadingText: "Loading", + textColor: Colors.black, + ), + ), + )); + } +} + +class LoaderColumn extends Column { + LoaderColumn({Key? key, String? loadingText, Color? color, Color? textColor}) + : super( + key: key, + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CircularProgressIndicator(color: color), + (loadingText == null || loadingText.isEmpty) + ? const SizedBox.shrink() + : Padding( + padding: REdgeInsets.only(top: 16.0), + child: CustomTextWidget( + text: loadingText!, + textAlign: TextAlign.center, + ), + ), + ], + ); +} diff --git a/lib/view/custom_widgets/multiline_text_field_sheet.dart b/lib/view/custom_widgets/multiline_text_field_sheet.dart new file mode 100644 index 0000000..61eaa44 --- /dev/null +++ b/lib/view/custom_widgets/multiline_text_field_sheet.dart @@ -0,0 +1,237 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; + +class MultilineTextFieldSheet { + final String appBarTitle; + final String textFieldHint; + final String buttonLabel; + final bool wantLeadingIcon; + final String prevValueForField; + final int minLines; + final int maxLines; + + // final ContentSheetAction action; + final Function(String text) onButtonClick; + + MultilineTextFieldSheet({ + required this.buttonLabel, + required this.appBarTitle, + required this.textFieldHint, + this.prevValueForField = "", + this.wantLeadingIcon = true, + required this.onButtonClick, + this.minLines = 5, + this.maxLines = 5, + }); + + show() { + Get.bottomSheet( + _ContentWidget( + key: const ValueKey(1), + appBarTitle: appBarTitle, + buttonLabel: buttonLabel, + onButtonClick: onButtonClick, + textFieldHint: textFieldHint, + prevValueForField: prevValueForField, + // action: action, + wantLeadingIcon: wantLeadingIcon, + minLines: minLines, + maxLines: maxLines, + ), + clipBehavior: Clip.none, + backgroundColor: Colors.transparent, + elevation: 4, + isScrollControlled: true, + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.vertical(top: Radius.circular(32)).r, + ), + ); + } +} + +class _ContentWidget extends StatefulWidget { + final String appBarTitle; + final String textFieldHint; + final String buttonLabel; + final String prevValueForField; + final bool wantLeadingIcon; + final Function onButtonClick; + final int minLines; + final int maxLines; + + const _ContentWidget({ + super.key, + required this.appBarTitle, + required this.buttonLabel, + required this.textFieldHint, + this.prevValueForField = "", + required this.onButtonClick, + required this.wantLeadingIcon, + this.minLines = 5, + this.maxLines = 5, + }); + + @override + _ContentWidgetState createState() => _ContentWidgetState(); +} + +class _ContentWidgetState extends State<_ContentWidget> { + final ContentController _controller = ContentController(); + + @override + void initState() { + _controller.initData(widget.prevValueForField, ""); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.white, + padding: MediaQuery.of(context).viewPadding, + child: ListView( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: [ + bottomSheetAppBar(context), + 24.verticalSpace, + _textField().addPaddingHorizontal(24), + 32.verticalSpace, + _addButton(context).addPaddingHorizontal(24), + 24.verticalSpace, + ], + ), + ); + } + + AppBar bottomSheetAppBar(BuildContext context) { + return AppBar( + automaticallyImplyLeading: false, + title: Text( + widget.appBarTitle, + style: const TextStyle(color: Colors.black), + ), + centerTitle: false, + titleSpacing: 24, + backgroundColor: Colors.transparent, + elevation: 0, + actions: [ + InkWell( + child: const Icon( + Icons.close, + color: Colors.black, + ), + onTap: () { + Navigator.pop(context); + }), + 16.horizontalSpace, + ], + ); + } + + Widget _textField() { + return DecoratedBox( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: (8).toRadius(), + border: Border.all(color: Colors.grey, width: 1)), + child: Padding( + padding: REdgeInsets.symmetric(horizontal: 16.0, vertical: 12), + child: TextField( + controller: _controller.textFieldTEC, + textCapitalization: TextCapitalization.sentences, + textInputAction: TextInputAction.next, + style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w400), + onChanged: (text) { + _controller.isButtonEnable( + text.isNotNullOrEmpty() && text != widget.prevValueForField); + }, + minLines: widget.minLines, + maxLines: widget.maxLines, + decoration: InputDecoration( + isDense: true, + hintText: widget.textFieldHint, + hintStyle: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w400) + .copyWith(color: Colors.grey), + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + errorBorder: InputBorder.none, + focusedErrorBorder: InputBorder.none, + ), + ), + ), + ); + } + + Widget _addButton(context) { + return InkWell( + onTap: () { + if (_controller.isButtonEnable()) { + Get.back(); + widget.onButtonClick(_controller.textFieldTEC.text); + } + }, + child: Obx(() { + return Container( + decoration: BoxDecoration( + color: _controller.isButtonEnable() + ? Get.theme.primaryColor + : Colors.grey.withOpacity(0.5), + borderRadius: BorderRadius.circular(4.r), + boxShadow: [ + BoxShadow( + color: Get.theme.primaryColor.withOpacity(0.2), + spreadRadius: _controller.isButtonEnable() ? 4 : 0, + blurRadius: _controller.isButtonEnable() ? 8 : 0, + offset: _controller.isButtonEnable() + ? const Offset(4, 8) + : const Offset(0, 0)) + ]), + width: double.maxFinite, + height: 48, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Visibility( + visible: widget.wantLeadingIcon, + child: const Padding( + padding: EdgeInsets.only(right: 8.0), + child: Icon( + Icons.add, + color: Colors.white, + size: 20, + ), + ), + ), + Text(widget.buttonLabel, + style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w700) + .copyWith(color: Colors.white)), + ], + ), + ); + }), + ); + } +} + +class ContentController extends GetxController { + final TextEditingController textFieldTEC = TextEditingController(); + final FocusNode textFieldFN = FocusNode(); + + final isButtonEnable = false.obs; + + initData(String prevText, String prevColor) { + textFieldTEC.text = (prevText.isNullOrEmptyNot()) ? prevText : ""; + } + + @override + void onClose() { + textFieldTEC.dispose(); + textFieldFN.unfocus(); + super.onClose(); + } +} diff --git a/lib/view/custom_widgets/my_circle_image.dart b/lib/view/custom_widgets/my_circle_image.dart new file mode 100644 index 0000000..eacdb68 --- /dev/null +++ b/lib/view/custom_widgets/my_circle_image.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'my_network_image.dart'; + +class MyCircleImage extends StatelessWidget { + final double imageSize; + final String url; + final Widget? loadingWidget; + final Widget? errorWidget; + final BoxFit? fit; + + const MyCircleImage( + {Key? key, + required this.imageSize, + required this.url, + this.loadingWidget, + this.errorWidget, + this.fit}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return ClipOval( + child: SizedBox( + width: imageSize, + height: imageSize, + child: MyNetworkImage( + url: url, + errorWidget: errorWidget, + loadingWidget: loadingWidget, + fit: fit, + ), + ), + ); + } +} diff --git a/lib/view/custom_widgets/my_network_image.dart b/lib/view/custom_widgets/my_network_image.dart new file mode 100644 index 0000000..b272a52 --- /dev/null +++ b/lib/view/custom_widgets/my_network_image.dart @@ -0,0 +1,77 @@ +import 'dart:io'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:get/utils.dart'; +import 'package:path/path.dart' as path; + +class MyNetworkImage extends StatelessWidget { + final String url; + final Widget? loadingWidget; + final Widget? errorWidget; + final BoxFit? fit; + final int? memCacheWidth; + final int? memCacheHeight; + + const MyNetworkImage({ + Key? key, + required this.url, + this.loadingWidget, + this.errorWidget, + this.fit, + this.memCacheWidth, + this.memCacheHeight, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final errWidget = + (errorWidget ?? ColoredBox(color: Colors.grey.withOpacity(0.3))); + return (url.isEmpty || !url.isImageFileName) + ? errWidget + : _media; + } + + Widget get _media { + return (path.extension(url).toLowerCase() == ".svg") + ? SvgPicture.network( + url, + fit: fit ?? BoxFit.cover, + placeholderBuilder: (_) { + return Center( + child: SizedBox.square( + dimension: 32, + child: (Platform.isIOS) + ? const CupertinoActivityIndicator(radius: 20) + : const CircularProgressIndicator( + strokeWidth: 2, + ), + ), + ); + }, + ) + : CachedNetworkImage( + imageUrl: url, + fit: fit ?? BoxFit.cover, + memCacheWidth: memCacheWidth, + memCacheHeight: memCacheHeight, + placeholder: (context, url) { + return loadingWidget ?? + Center( + child: SizedBox.square( + dimension: 32, + child: (Platform.isIOS) + ? const CupertinoActivityIndicator(radius: 20) + : const CircularProgressIndicator( + strokeWidth: 2, + ), + ), + ); + }, + errorWidget: (context, url, error) { + return errorWidget ?? Icon(Icons.image_not_supported_outlined); + }, + ); + } +} diff --git a/lib/view/custom_widgets/notifications/custom_dialog_notification.dart b/lib/view/custom_widgets/notifications/custom_dialog_notification.dart new file mode 100644 index 0000000..08dc082 --- /dev/null +++ b/lib/view/custom_widgets/notifications/custom_dialog_notification.dart @@ -0,0 +1,120 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + + +class ShowDialogNotification extends StatelessWidget { + const ShowDialogNotification({ + super.key, + required this.rotaShift, + }); + + final RotaShift rotaShift; + + @override + Widget build(BuildContext context) { + return AlertDialog( + surfaceTintColor: CustomAppColors.kPrimaryColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2)), + backgroundColor: CustomAppColors.kPrimaryColor, + title: Center( + child: CustomTextWidget( + text: rotaShift.managerName, + fontWeight: FontWeight.bold, + isExpanded: false, + alignment: Alignment.center, + fontSize: 16.sp, + ), + ), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + NotificationInfoRow( label: 'Service User:', value: rotaShift.name,), + const SizedBox(height: 5,), + NotificationInfoRow(label: 'Worker Type Role:', value: rotaShift.workerType), + const SizedBox(height: 5,), + NotificationInfoRow(label: 'Location:',value: rotaShift.location), + const SizedBox(height: 5,), + NotificationInfoRow(label: 'Start Time:', value: rotaShift.startTime), + const SizedBox(height: 5,), + NotificationInfoRow(label: 'End Time:',value: rotaShift.endTime), + const SizedBox(height: 5,), + NotificationInfoRow(label: 'Break Time:',value: rotaShift.breakTime), + const SizedBox(height: 5,), + NotificationInfoRow(label: 'Notes:',value: rotaShift.notes), + ], + ), + actions: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Visibility( + visible: false, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + maximumSize: Size(MediaQuery.of(context).size.width/3.5, 30.h), + minimumSize: Size(MediaQuery.of(context).size.width/3.5, 30.h), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.r), + ), + backgroundColor: CustomAppColors.kRedColor + ), + onPressed: () { + // Add functionality for the "Decline" button. + Navigator.of(context).pop(); + }, + child: const CustomTextWidget(text: 'Decline',fontColor: CustomAppColors.kPrimaryColor), + ), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + // maximumSize: Size(MediaQuery.of(context).size.width/3.5, 30.h), + maximumSize: Size(MediaQuery.of(context).size.width/1.8, 30.h), + // minimumSize: Size(MediaQuery.of(context).size.width/3.5, 30.h), + minimumSize: Size(MediaQuery.of(context).size.width/1.8, 30.h), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.r), + ), + backgroundColor: CustomAppColors.kPrimaryColor + ), + onPressed: () { + // Add functionality for the "Close" button. + Navigator.of(context).pop(); + }, + child: CustomTextWidget(text: 'Close',fontColor: CustomAppColors.kBlackColor,fontSize: 15.sp), + ), + ], + ), + ], + ); + } +} + +class NotificationInfoRow extends StatelessWidget { + const NotificationInfoRow({ + super.key,required this.label, required this.value, this.half = false + }); + + final String label, value; + final bool half; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + border: Border.all(width: 1.w, color: CustomAppColors.kLightGreyColor), + borderRadius: BorderRadius.circular(5.r)), + padding: EdgeInsets.only(left: 10.w,top: 1.h,bottom: 1.h), + width: half ? null : MediaQuery.of(context).size.width, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTextWidget(text: label,isExpanded: false,fontSize: 12.sp), + CustomTextWidget(text: value,fontWeight: FontWeight.w600,isExpanded: false,fontSize: 14,), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/view/custom_widgets/notifications/export_notification_widgets.dart b/lib/view/custom_widgets/notifications/export_notification_widgets.dart new file mode 100644 index 0000000..6606b80 --- /dev/null +++ b/lib/view/custom_widgets/notifications/export_notification_widgets.dart @@ -0,0 +1,2 @@ +export 'custom_dialog_notification.dart'; +export 'holiday_request_accept_dialog.dart'; \ No newline at end of file diff --git a/lib/view/custom_widgets/notifications/holiday_request_accept_dialog.dart b/lib/view/custom_widgets/notifications/holiday_request_accept_dialog.dart new file mode 100644 index 0000000..fe250ae --- /dev/null +++ b/lib/view/custom_widgets/notifications/holiday_request_accept_dialog.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + + +class HolidayRequestAcceptDialog extends StatelessWidget { + const HolidayRequestAcceptDialog({ + super.key, required this.startDate, required this.endDate, required this.noOfDays, + }); + + final String startDate; + final String endDate; + final String noOfDays; + + @override + Widget build(BuildContext context) { + return AlertDialog( + insetPadding: EdgeInsets.only(right: 18.w,left: 18.w, top:40.h ,bottom: 230.h), + contentPadding: EdgeInsets.only(left: 15.w,right: 15.w,top: 11.h,bottom: 0.h), + surfaceTintColor: CustomAppColors.kPrimaryColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2)), + backgroundColor: CustomAppColors.kPrimaryColor, + title: Center( + child: CustomTextWidget( + text: "Your Holiday Request has been sent", + fontWeight: FontWeight.w700, + isExpanded: false, + alignment: Alignment.center, + fontSize: 14.sp, + ), + ), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded(child: DateInfo( label: 'Start Date', value: startDate,half: true)), + SizedBox(width: 5.w,), + Expanded(child: DateInfo(label: 'End Date', value: endDate,half: true)), + ], + ), + SizedBox(height: 10.h,), + DateInfo(label: 'Holiday Total Time',value: noOfDays), + SizedBox(height: 10.h,), + CustomTextWidget(text: "Kindly wait as we review your holiday request.",fontSize: 12.sp), + SizedBox(height: 10.h,), + + ], + ), + actions: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.r), + ), + backgroundColor: CustomAppColors.kPrimaryColor, + surfaceTintColor: CustomAppColors.kPrimaryColor, + ), + onPressed: () { + // Add functionality for the "Close" button. + Navigator.of(context).pop(); + }, + child: CustomTextWidget( + text: 'Close', + fontColor: CustomAppColors.kBlackColor, + fontSize: 15.sp), + ), + ], + ); + } +} + +class DateInfo extends StatelessWidget { + const DateInfo({ + super.key,required this.label, required this.value, this.half = false + }); + + final String label, value; + final bool half; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + border: Border.all(width: 1.w, color: CustomAppColors.kLightGreyColor), + borderRadius: BorderRadius.circular(5.r)), + padding: EdgeInsets.only(left: 10.w,top: 1.h,bottom: 1.h), + width: half ? null : MediaQuery.of(context).size.width, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTextWidget(text: label, isExpanded: false, fontSize: 12.sp), + CustomTextWidget( + text: value, + fontWeight: FontWeight.w600, + isExpanded: false, + fontSize: 14, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/view/custom_widgets/rota/custom_calendar_widget.dart b/lib/view/custom_widgets/rota/custom_calendar_widget.dart new file mode 100644 index 0000000..6ecc6ef --- /dev/null +++ b/lib/view/custom_widgets/rota/custom_calendar_widget.dart @@ -0,0 +1,321 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_calendar_carousel/classes/event.dart'; +import 'package:flutter_calendar_carousel/flutter_calendar_carousel.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import '../../../ftc_mobile_app.dart'; + +// class SetMonthAndYearWidget extends StatelessWidget { +// const SetMonthAndYearWidget({ +// super.key, +// required this.currentMonthName, +// required this.previousMonthTap, +// required this.nextMonthTap, +// }); +// +// final String currentMonthName; +// final VoidCallback previousMonthTap; +// final VoidCallback nextMonthTap; +// +// @override +// Widget build(BuildContext context) { +// return Container( +// padding: const EdgeInsets.only(left: 20.0, right: 20.0), +// child: Row( +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// children: [ +// GestureDetector( +// onTap: previousMonthTap, +// child: Transform.rotate( +// angle: 3.1415 * 2.5, +// child: const Icon(Icons.arrow_drop_down), +// ), +// ), +// CustomTextWidget( +// isExpanded: false, +// text: currentMonthName, +// textAlign: TextAlign.center, +// fontColor: CustomAppColors.kBlackColor, +// fontSize: 13, +// fontWeight: FontWeight.bold), +// GestureDetector( +// onTap: nextMonthTap, +// child: Transform.rotate( +// angle: 3.1415 * 3.5, +// child: const Icon(Icons.arrow_drop_down), +// ), +// ), +// ], +// ), +// ); +// } +// } + +typedef OnRangeSelect = Function( + DateTime rangeStart, DateTime? rangeEnd, List selectedDates); + +class CalendarWidget extends StatefulWidget { + static final dateFormat = DateFormat("dd-MM-yyyy"); + + const CalendarWidget({ + super.key, + this.minDate, + this.maxDate, + this.onEventTap, + required this.targetDateTime, + required this.markedDatesMap, + this.onDayTap, + this.rangeStart, + this.rangeEnd, + this.canSelectRange = false, + this.onRangeSelect, + }); + + final DateTime? minDate; + final DateTime? maxDate; + final DateTime targetDateTime; + final EventList markedDatesMap; + final ValueChanged>? onEventTap; + final Function(DateTime, List)? onDayTap; + final bool canSelectRange; + + ///[selectedDates] will be in format dd-MM-yyyy + final OnRangeSelect? onRangeSelect; + final DateTime? rangeStart; + final DateTime? rangeEnd; + + @override + State createState() => _CalendarWidgetState(); + + ///This method checks if [start] and [end] dates are at proper position i.e., + ///[start] date should be before [end] date. If not, this method corrects the two dates and + ///return proper [start] [end] dates + static ({DateTime start, DateTime end}) rectifyStartEndDates( + DateTime start, DateTime end) { + final first = start.isBefore(end) ? start : end; + final last = end.isAfter(start) ? end : start; + return (start: first, end: last); + } + + static List getDatesFromStartEndRange(DateTime start, DateTime end) { + final List dates = []; + // final first = start.isBefore(end) ? start : end; + // final last = end.isAfter(start) ? end : start; + + final record = rectifyStartEndDates(start, end); + for (int i = 0; i <= record.end.difference(record.start).inDays; i++) { + dates.add(dateFormat.format(record.start.add(i.days))); + } + return dates; + } + + static Widget shiftIcon(String day) => CircleAvatar( + backgroundColor: CustomAppColors.kSecondaryColor, + child: Text( + day, + style: + const TextStyle(color: CustomAppColors.kWhiteColor, fontSize: 13), + ), + ); + + static Widget underlineIcon() => Container( + alignment: Alignment.center, + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide( + color: CustomAppColors.kSecondaryColor, + width: 3.0, + ), + ), + ), + ); +} + +class _CalendarWidgetState extends State { + //Selected dates will be in format dd-MM-yyyy + final selectedDates = RxList(); + + DateTime? start, end; + + @override + void initState() { + super.initState(); + + if (widget.canSelectRange && + widget.rangeStart != null && + widget.rangeEnd != null) { + start = widget.rangeStart; + end = widget.rangeEnd; + + selectedDates.value = + CalendarWidget.getDatesFromStartEndRange(start!, end!); + } else { + selectedDates.value = [ + CalendarWidget.dateFormat.format(widget.targetDateTime) + ]; + } + } + + @override + Widget build(BuildContext context) { + double width = MediaQuery.of(context).size.width; + double height = MediaQuery.of(context).size.height; + double textScaleFactor = MediaQuery.of(context).textScaleFactor; + double itemHeight = (height * textScaleFactor - kToolbarHeight - 24) / 4; + double calHeight = (height * textScaleFactor) / 2.9; + double itemWidth = width * 0.7; + + return Container( + height: calHeight, + margin: const EdgeInsets.only(top: 10, bottom: 3), + padding: const EdgeInsets.only(left: 10.0, right: 10.0), + child: CalendarCarousel( + targetDateTime: widget.targetDateTime, + firstDayOfWeek: 0, + dayPadding: 1.0, + minSelectedDate: widget.minDate, + maxSelectedDate: widget.maxDate, + todayTextStyle: const TextStyle( + color: CustomAppColors.kBlackColor, + fontWeight: FontWeight.normal, + fontSize: 13.0, + ), + weekdayTextStyle: const TextStyle( + color: CustomAppColors.kBlueColor, + fontWeight: FontWeight.bold, + fontSize: 13.0, + ), + weekendTextStyle: const TextStyle( + color: CustomAppColors.kBlackColor, + fontSize: 13.0, + ), + customDayBuilder: (bool isSelectable, + int index, + bool isSelectedDay, + bool isToday, + bool isPrevMonthDay, + TextStyle textStyle, + bool isNextMonthDay, + bool isThisMonthDay, + DateTime day) { + if (selectedDates.contains(CalendarWidget.dateFormat.format(day))) { + return Container( + width: double.maxFinite, + height: double.maxFinite, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context).primaryColor, + ), + child: Center( + child: Text( + day.day.toString(), + style: textStyle.copyWith(color: Colors.white), + ), + ), + ); + } + return null; + }, + onDayPressed: (DateTime date, List events) { + if (widget.onEventTap != null) { + widget.onEventTap!(events); + } + if (widget.onDayTap != null) { + widget.onDayTap!(date, events); + } + if (widget.canSelectRange) { + rangeSelectionHandler(date); + } + }, + childAspectRatio: textScaleFactor > 1.5 + ? ((itemWidth / itemHeight) * 1.1) + : textScaleFactor > 1 + ? ((itemWidth / itemHeight) * 1.05) + : (itemWidth / itemHeight), + todayButtonColor: Colors.transparent, + todayBorderColor: Colors.transparent, + daysHaveCircularBorder: true, + isScrollable: true, + shouldShowTransform: false, + pageSnapping: true, + // selectedDateTime: DateTime(2024, 5, 25), + thisMonthDayBorderColor: Colors.transparent, + daysTextStyle: const TextStyle( + fontSize: 13.0, + color: CustomAppColors.kBlackColor, + ), + weekFormat: false, + showOnlyCurrentMonthDate: true, + width: width, + onHeaderTitlePressed: () {}, + customGridViewPhysics: const NeverScrollableScrollPhysics(), + markedDatesMap: widget.markedDatesMap, + //marked up dates + markedDateIconBuilder: (event) => event.icon, + markedDateCustomTextStyle: TextStyle( + fontSize: 13.sp, + color: CustomAppColors.kWhiteColor, + ), + markedDateShowIcon: true, + markedDateIconMaxShown: 1, + markedDateMoreShowTotal: null, + showHeader: true, + leftButtonIcon: const Icon( + Icons.arrow_left, + color: Colors.black, + ), + rightButtonIcon: const Icon( + Icons.arrow_right, + color: Colors.black, + ), + headerTextStyle: const TextStyle( + color: CustomAppColors.kBlackColor, + fontSize: 13, + fontWeight: FontWeight.bold), + onCalendarChanged: (DateTime date) {}, + onDayLongPressed: (DateTime date) {}, + ), + ); + } + + rangeSelectionHandler(DateTime selectedDay) { + final d = CalendarWidget.dateFormat.format(selectedDay); + + if (selectedDates.contains(d)) { + selectedDates.remove(d); + } else { + selectedDates.add(d); + } + + //meaning either no range selected or range already been selected + if (start == null || (start != null && end != null)) { + start = selectedDay; + end = null; + selectedDates + ..clear() + ..add(CalendarWidget.dateFormat.format(selectedDay)); + + if (widget.onRangeSelect != null) { + widget.onRangeSelect!(start!, null, selectedDates()); + } + return; + } + + //Already selected start date now selecting range end date + if (end == null) { + final record = CalendarWidget.rectifyStartEndDates(start!, selectedDay); + + start = record.start; + end = record.end; + + selectedDates.clear(); + selectedDates.value = + CalendarWidget.getDatesFromStartEndRange(start!, end!); + } + print("start: ${start.toString()}\nEnd:${end.toString()}"); + + if (widget.onRangeSelect != null) { + widget.onRangeSelect!(start!, end, selectedDates()); + } + } +} diff --git a/lib/view/custom_widgets/rota/export_rota.dart b/lib/view/custom_widgets/rota/export_rota.dart new file mode 100644 index 0000000..9a5f46c --- /dev/null +++ b/lib/view/custom_widgets/rota/export_rota.dart @@ -0,0 +1,3 @@ +export 'custom_calendar_widget.dart'; +export "package:ftc_mobile_app/dialogs/widgets/shift_dialog.dart"; +export 'rota_list_item.dart'; \ No newline at end of file diff --git a/lib/view/custom_widgets/rota/rota_list_item.dart b/lib/view/custom_widgets/rota/rota_list_item.dart new file mode 100644 index 0000000..9ff96d2 --- /dev/null +++ b/lib/view/custom_widgets/rota/rota_list_item.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/rota/WeekArrayData.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import '../../../ftc_mobile_app.dart'; + +class RotaWidget extends StatelessWidget { + const RotaWidget( + {super.key, required this.data, required this.onViewShiftTap}); + + final DaysArrayData data; + final Function() onViewShiftTap; + + @override + Widget build(BuildContext context) { + return Container( + // height: 150, + alignment: Alignment.center, + padding: EdgeInsets.all(10.sp), + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: Colors.white, + border: Border( + left: BorderSide( + width: 6.r, + color: CustomAppColors.kSecondaryColor, + ), + top: const BorderSide( + color: CustomAppColors.kSecondaryColor, + ), + right: const BorderSide( + color: CustomAppColors.kSecondaryColor, + ), + bottom: const BorderSide( + color: CustomAppColors.kSecondaryColor, + ), + ), + borderRadius: BorderRadius.all(Radius.circular(2.0.r)), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text.rich( + TextSpan( + text: data.locationId?.shiftLocationName ?? "", + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w400, + ), + children: [ + const TextSpan(text: ' ('), + TextSpan( + text: ((data.serviceUserId?.displayName ?? + "Unassigned")) + .trim(), + style: TextStyle( + color: data.serviceUserId?.displayName + .isNotNullOrEmpty() == + true + ? Colors.black + : Colors.red)), + const TextSpan(text: ')'), + ], + ), + ), + ), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + CustomTextWidget( + fontWeight: FontWeight.w500, + fontSize: 10.sp, + text: DateFormatter().getRotaNewDate( + shiftDate: data.shiftDate ?? 0, + startShiftTime: data.shiftStartTime ?? '0', + endShiftTime: data.shiftEndTime ?? '0', + ), + isExpanded: false), + 6.verticalSpace, + CustomTextWidget( + text: '${int.parse(data.workHrs?.toString() ?? "0")}hrs', + fontSize: 14.sp, + fontWeight: FontWeight.w600, + isExpanded: false, + ), + ], + ), + ], + ), + 10.verticalSpace, + ShiftButtonWidget(onTap: onViewShiftTap) + ], + ), + ); + } +} + +class ShiftButtonWidget extends StatelessWidget { + final VoidCallback onTap; + + const ShiftButtonWidget({ + super.key, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Container( + width: MediaQuery.of(context).size.width, + height: 26.h, + alignment: Alignment.center, + decoration: BoxDecoration( + color: CustomAppColors.kSecondaryColor, + borderRadius: BorderRadius.circular(2.r), + ), + child: const Text( + "View Shift", + style: TextStyle(color: CustomAppColors.kPrimaryColor), + ), + ), + ); + } +} diff --git a/lib/view/export_view.dart b/lib/view/export_view.dart new file mode 100644 index 0000000..fc94be5 --- /dev/null +++ b/lib/view/export_view.dart @@ -0,0 +1,2 @@ +export 'custom_widgets/export_custom_widgets.dart'; +export 'screens/export_screens.dart'; \ No newline at end of file diff --git a/lib/view/screens/auth_module/agency_sign_in.dart b/lib/view/screens/auth_module/agency_sign_in.dart new file mode 100644 index 0000000..89a85c5 --- /dev/null +++ b/lib/view/screens/auth_module/agency_sign_in.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; + +class AgencySignIn extends StatefulWidget { + const AgencySignIn({super.key}); + + @override + State createState() => _AgencySignInState(); +} + +class _AgencySignInState extends State { + final AgencySignInController _controller = Get.put(AgencySignInController()); + + + @override + Widget build(BuildContext context) { + return CustomScaffold( + screenKey: _controller.screenKey, + onScreenTap: _controller.removeFocus, + showAppBar: true, + titleText: "", + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 15.0.w), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CustomImageWidget( + imagePath: AssetsManager.kAppIcon, + imageColor: CustomAppColors.kIconColor, + height: 132.h, + width: 203.w, + ), + + CustomTextWidget( + text: ConstantText.kAgencyLogin, + fontColor: CustomAppColors.kIconColor, + fontWeight: FontWeight.w700, + fontSize: 24.sp, + ), + + CustomTextWidget( + text: ConstantText.kPleaseLoginToContinue, + fontColor: CustomAppColors.kIconColor, + fontWeight: FontWeight.w400, + fontSize: 14.sp, + ), + + Padding( + padding: EdgeInsets.only(top: 30.0.h), + child: CustomTextFieldWidget( + controller: _controller.emailPhoneController, + hintText: ConstantText.kInputEmailOrPhone, + heading: ConstantText.kEmailOrPhoneHeading, + onChange: (_){ + _controller.validateEmailPhone(); + }, + ), + ), + Obx((){ + return CustomErrorMsg( + message: _controller.emailPhoneErrorMsg.value, + ); + }), + + Padding( + padding: EdgeInsets.only(top: 25.0.h), + child: Obx((){ + return CustomAppButton( + isLoading: _controller.isLoading.value, + buttonText: ConstantText.kSendCode.toUpperCase(), + onTap: _controller.onSendCodeButton, + ); + }), + ), + ], + ), + ), + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/auth_module/export_auth_module.dart b/lib/view/screens/auth_module/export_auth_module.dart new file mode 100644 index 0000000..aced0a1 --- /dev/null +++ b/lib/view/screens/auth_module/export_auth_module.dart @@ -0,0 +1,4 @@ +export 'splash_screen.dart'; +export 'sign_in_screen.dart'; +export 'agency_sign_in.dart'; +export 'otp_screen.dart'; \ No newline at end of file diff --git a/lib/view/screens/auth_module/otp_screen.dart b/lib/view/screens/auth_module/otp_screen.dart new file mode 100644 index 0000000..bc60c14 --- /dev/null +++ b/lib/view/screens/auth_module/otp_screen.dart @@ -0,0 +1,124 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; +import 'package:pin_code_fields/pin_code_fields.dart'; + +class OTPScreen extends StatefulWidget { + const OTPScreen({super.key}); + + @override + State createState() => _OTPScreenState(); +} + +class _OTPScreenState extends State { + final OTPScreenController _controller = Get.put(OTPScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + screenKey: _controller.screenKey, + onScreenTap: _controller.removeFocus, + avoidBottomInsets: false, + showAppBar: true, + titleText: "", + body: Padding( + padding: EdgeInsets.symmetric(horizontal: 15.0.w), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + CustomImageWidget( + imagePath: AssetsManager.kLockIcon, + imageColor: CustomAppColors.kIconColor, + height: 84.h, + width: 75.w, + ), + + Padding( + padding: EdgeInsets.only(top: 30.0.h), + child: CustomTextWidget( + text: ConstantText.kTwoFactorAuth, + fontColor: CustomAppColors.kIconColor, + fontWeight: FontWeight.w700, + fontSize: 24.sp, + alignment: Alignment.centerLeft, + ), + ), + + CustomTextWidget( + text: ConstantText.kOTPScreenMsg, + fontColor: CustomAppColors.kIconColor, + fontWeight: FontWeight.w400, + fontSize: 14.sp, + alignment: Alignment.centerLeft, + textAlign: TextAlign.start, + ), + + Padding( + padding: EdgeInsets.only(top: 20.0.h), + child: PinCodeTextField( + keyboardType: TextInputType.number, + textInputAction: TextInputAction.done, + autoDisposeControllers: false, + controller: _controller.otpController, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + onAutoFillDisposeAction: AutofillContextAction.cancel, + appContext: context, + length: 6, + onChanged: (otp) { + _controller.validateOTP(); + }, + cursorColor: CustomAppColors.kIconColor, + textStyle: TextStyle( + color: CustomAppColors.kIconColor.withOpacity(0.6), + ), + pinTheme: PinTheme( + shape: PinCodeFieldShape.box, + borderRadius: BorderRadius.circular(2.r), + fieldHeight: 56.0.h, + fieldWidth: 43.33.w, + borderWidth: 0.5.w, + fieldOuterPadding: EdgeInsets.symmetric(vertical: 10.0.h,), + activeFillColor: CustomAppColors.kIconColor.withOpacity(0.6), + activeColor: CustomAppColors.kIconColor.withOpacity(0.6), + errorBorderColor: CustomAppColors.kIconColor.withOpacity(0.6), + selectedColor: CustomAppColors.kIconColor.withOpacity(0.6), + inactiveColor: CustomAppColors.kIconColor.withOpacity(0.6), + selectedFillColor: CustomAppColors.kIconColor.withOpacity(0.6), + ), + ), + ), + Obx(() { + return CustomErrorMsg( + message: _controller.otpErrorMsg.value, + ); + }), + const Spacer(), + + Padding( + padding: EdgeInsets.only( + bottom: Platform.isIOS ? 30.0.h : 20.0.h, + ), + child: Obx((){ + return CustomAppButton( + isLoading: _controller.isLoading.value, + buttonText: ConstantText.kSubmit.toUpperCase(), + onTap: _controller.onSubmitButton, + ); + }), + ), + ], + ), + ), + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/auth_module/sign_in_screen.dart b/lib/view/screens/auth_module/sign_in_screen.dart new file mode 100644 index 0000000..e81e522 --- /dev/null +++ b/lib/view/screens/auth_module/sign_in_screen.dart @@ -0,0 +1,160 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; + +class SignInScreen extends StatefulWidget { + const SignInScreen({super.key}); + + @override + State createState() => _SignInScreenState(); +} + +class _SignInScreenState extends State { + final SignInScreenController _controller = Get.put(SignInScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + screenKey: _controller.screenKey, + onScreenTap: _controller.removeFocus, + showAppBar: true, + titleText: "", + body: Padding( + padding: EdgeInsets.symmetric(horizontal: 15.0.w), + child: Column( + children: [ + Expanded( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CustomImageWidget( + imagePath: AssetsManager.kAppIcon, + imageColor: CustomAppColors.kIconColor, + height: 132.h, + width: 203.w, + ), + + CustomTextWidget( + text: ConstantText.kWelcomeBack, + fontColor: CustomAppColors.kIconColor, + fontWeight: FontWeight.w700, + fontSize: 24.sp, + ), + + CustomTextWidget( + text: ConstantText.kPleaseLoginToContinue, + fontColor: CustomAppColors.kIconColor, + fontWeight: FontWeight.w400, + fontSize: 14.sp, + ), + + Padding( + padding: EdgeInsets.only(top: 30.0.h), + child: CustomTextFieldWidget( + controller: _controller.emailController, + hintText: ConstantText.kPleaseInputEmail, + heading: ConstantText.kEmailHeading, + onChange: (_){ + _controller.validateEmail(); + }, + ), + ), + Obx((){ + return CustomErrorMsg( + message: _controller.emailErrorMsg.value, + ); + }), + + Padding( + padding: EdgeInsets.only(top: 15.0.h), + child: CustomTextFieldWidget( + controller: _controller.passwordController, + hintText: "******", + heading: ConstantText.kPasswordHeading, + onChange: (_){ + _controller.validatePassword(); + }, + isObscure: true, + maxLines: 1, + ), + ), + Obx((){ + return CustomErrorMsg( + message: _controller.passwordErrorMsg.value, + ); + }), + Padding( + padding: EdgeInsets.only( + top: 15.0.h, + bottom: 25.h, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Obx((){ + // return CustomCheckBox( + // checkBoxValue: _controller.isRememberMe.value, + // titleText: ConstantText.kRememberMe, + // onTap: () { + // _controller.isRememberMe.toggle(); + // }, + // ); + // }), + + // const Spacer(), + GestureDetector( + onTap: _controller.onForgotButton, + child: CustomTextWidget( + text: ConstantText.kForgotPassword, + isExpanded: false, + fontSize: 12.sp, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + + Padding( + padding: EdgeInsets.only(bottom: 8.0.h), + child: Obx((){ + return CustomAppButton( + isLoading: _controller.isLoading.value, + buttonText: ConstantText.kLogIn.toUpperCase(), + onTap: _controller.onLogInButton, + ); + }), + ), + ], + ), + ), + ), + // Padding( + // padding: EdgeInsets.only( + // bottom: Platform.isIOS ? 30.0.h : 20.0.h, + // ), + // child: CustomAppButton( + // buttonText: ConstantText.kAgencyLogin.toUpperCase(), + // buttonColor: CustomAppColors.kPrimaryColor, + // textColor: CustomAppColors.kSecondaryColor, + // onTap: _controller.onAgencyLogInButton, + // ), + // ), + ], + ), + ), + ); + } + + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/auth_module/splash_screen.dart b/lib/view/screens/auth_module/splash_screen.dart new file mode 100644 index 0000000..95f7693 --- /dev/null +++ b/lib/view/screens/auth_module/splash_screen.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/utilities/notification_util.dart'; +import 'package:get/get.dart'; + +class SplashScreen extends StatefulWidget { + const SplashScreen({super.key}); + + @override + State createState() => _SplashScreenState(); +} + +class _SplashScreenState extends State { + final _controller = Get.put(SplashScreenController()); + + @override + void initState() { + requestNotificationPermissions(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + screenKey: _controller.screenKey, + backgroundColor: CustomAppColors.kSecondaryColor, + body: const Center( + child: CustomImageWidget( + imagePath: AssetsManager.kAppIcon, + height: 200, + ), + ), + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/chat/arguments/chat_screen_args.dart b/lib/view/screens/chat/arguments/chat_screen_args.dart new file mode 100644 index 0000000..26e7883 --- /dev/null +++ b/lib/view/screens/chat/arguments/chat_screen_args.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/chat/ChatModel.dart'; +import 'group_data_args.dart'; + +class ChatScreenArgs { + final String name; + final String profilePicPath; + final String otherUserId; + final GroupDataArgs? groupData; + + final ValueChanged? onLastMessageUpdate; + + ChatScreenArgs({ + required this.name, + required this.profilePicPath, + required this.otherUserId, + this.groupData, + this.onLastMessageUpdate, + }); +} diff --git a/lib/view/screens/chat/arguments/group_data_args.dart b/lib/view/screens/chat/arguments/group_data_args.dart new file mode 100644 index 0000000..bfb3f65 --- /dev/null +++ b/lib/view/screens/chat/arguments/group_data_args.dart @@ -0,0 +1,13 @@ +import 'package:ftc_mobile_app/models/chat/combined_last_messages_model_class.dart'; + +class GroupDataArgs { + final String groupId; + final List groupMembersIds; + final GroupWorkingScheduleTime scheduleTime; + + GroupDataArgs({ + required this.groupId, + required this.groupMembersIds, + required this.scheduleTime, + }); +} diff --git a/lib/view/screens/chat/chat_screen.dart b/lib/view/screens/chat/chat_screen.dart new file mode 100644 index 0000000..83aa845 --- /dev/null +++ b/lib/view/screens/chat/chat_screen.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/chat/ChatModel.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/my_circle_image.dart'; +import 'package:get/get.dart'; +import 'package:grouped_list/grouped_list.dart'; +import 'package:intl/intl.dart'; +import 'arguments/chat_screen_args.dart'; +import 'widgets/chat_screen_footer_widget.dart'; +import 'widgets/message_bubble.dart'; + +class ChatScreen extends StatefulWidget { + final ChatScreenArgs args; + + const ChatScreen({Key? key, required this.args}) : super(key: key); + + @override + State createState() => _ChatScreenState(); +} + +class _ChatScreenState extends State { + late final ChatScreenController controller; + final sepFormatter = DateFormat("dd MMM yyyy"); + + @override + void initState() { + controller = Get.put(ChatScreenController(widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: Colors.white, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: _appBar, + body: SafeArea( + child: Column( + children: [ + Expanded(child: messagesList()), + Obx(() { + return controller.isSocketConnected() + ? ChatScreenFooterWidget( + controller: controller, + enabled: controller.isSocketConnected(), + ) + : FrequentFunctions.noWidget; + }) + ], + ), + ), + ); + } + + AppBar get _appBar { + return AppBar( + toolbarHeight: 56.r, + leading: IconButton( + icon: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + onPressed: () { + Navigator.pop(context); + }, + ), + centerTitle: false, + titleSpacing: 0, + leadingWidth: 50.r, + surfaceTintColor: Colors.white, + title: Row( + mainAxisSize: MainAxisSize.min, + children: [ + MyCircleImage( + imageSize: 32.r, + url: "${WebUrls.baseUrl}${widget.args.profilePicPath}", + errorWidget: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + imageColor: CustomAppColors.kDarkBlueTextColor, + height: 32.r, + width: 32.r, + ), + ), + 10.horizontalSpace, + CustomTextWidget( + text: widget.args.name, + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ); + } + + Widget messagesList() { + return Obx(() { + final messages = controller.messages; + return (messages.isEmpty) + ? FrequentFunctions.noWidget + : _messagesList(messages()); + }); + } + + Widget _messagesList(List list) { + final now = DateTime.now(); + + return GroupedListView( + reverse: true, + elements: list, + padding: REdgeInsets.symmetric(horizontal: 18), + order: GroupedListOrder.DESC, + groupBy: (message) { + final messageDate = + DateTime.fromMillisecondsSinceEpoch(message.date ?? 0); + return DateFormatter.dateFormatter.format(messageDate); + }, + groupSeparatorBuilder: (String date) { + final isToday = (date == DateFormatter.dateFormatter.format(now)); + return _buildGroupSeparatorWidget(isToday + ? "Today" + : sepFormatter.format(DateFormatter.dateFormatter.parse(date))); + }, + itemBuilder: (_, e) => _buildItemWidget(list.indexOf(e), e), + sort: false, + separator: 8.verticalSpace, + ); + } + + Widget _buildGroupSeparatorWidget(String susTag) { + return Padding( + padding: REdgeInsets.symmetric(vertical: 12.0), + child: Row( + children: [ + Expanded( + child: Divider( + height: 1, + color: Colors.grey.withOpacity(0.3), + )), + Text( + susTag, + style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w500), + ).addPaddingHorizontal(10), + Expanded( + child: Divider( + height: 1, + color: Colors.grey.withOpacity(0.3), + )), + ], + ), + ); + } + + Widget _buildItemWidget(int index, ChatModel message) { + final isMyMessage = (message.from?.id == controller.myId); + final hasFile = message.filePath.isNotNullOrEmpty(); + print("filePath: ${message.filePath}"); + + return MessageBubble( + senderName: message.from?.name ?? "", + profilePic: !isMyMessage ? (message.from?.profilePictureUrl ?? "") : "", + content: hasFile ? (message.filePath ?? '') : (message.message ?? ''), + type: isMyMessage ? MessageType.sent : MessageType.received, + contentType: hasFile + ? (message.fileType == ChatModel.fileTypeLocalPath) + ? MessageContentType.file + : MessageContentType.url + : MessageContentType.text, + sentMessageColor: CustomAppColors.kSmokeColor, + receivedMessageColor: CustomAppColors.kSecondaryColor, + messageTime: (message.date == null) + ? "" + : DateTime.fromMillisecondsSinceEpoch(message.date!) + .toIso8601String(), + state: MessageState.stateNone, + status: MessageSeenStatus.seen, + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/chat/controller/chat_screen_controller.dart b/lib/view/screens/chat/controller/chat_screen_controller.dart new file mode 100644 index 0000000..86d5589 --- /dev/null +++ b/lib/view/screens/chat/controller/chat_screen_controller.dart @@ -0,0 +1,503 @@ +import 'dart:developer'; +import 'dart:io'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:ftc_mobile_app/controllers/home/inbox_screen_controller.dart'; +import 'package:ftc_mobile_app/models/chat/all_group_messages_model.dart'; +import 'package:ftc_mobile_app/models/chat/single_chat.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/utilities/frequent_functions.dart'; +import 'package:ftc_mobile_app/utilities/image_picker_popup.dart'; +import 'package:ftc_mobile_app/utilities/local_storage_manager/export_local_storage.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/home/custom_message_dialog.dart'; +import 'package:ftc_mobile_app/view/screens/chat/arguments/chat_screen_args.dart'; +import 'package:ftc_mobile_app/web_services/chat_services.dart'; +import 'package:ftc_mobile_app/web_services/web_url.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import 'package:socket_io_client/socket_io_client.dart'; +import '../../../../models/chat/ChatModel.dart'; + +class ChatScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final messageTEC = TextEditingController(); + final messageFN = FocusNode(); + + final messages = RxList(); + final isSocketConnected = false.obs; + + late final ChatScreenArgs args; + + String myId = ""; + + ChatScreenController(ChatScreenArgs data) { + args = data; + } + + late Socket _socketIO; + String listenId = ""; + + @override + void onInit() { + //Getting my ID + // String userJson = LocalStorageManager.getSessionToken( + // tokenKey: LocalStorageKeys.kUserModelKey, + // ); + // UserModel userModel = UserModel.fromJson(json.decode(userJson)); + myId = LocalStorageManager.userId; + + if (_canChat) { + initializeSocket(); + } + + super.onInit(); + } + + @override + void onReady() { + if (isGroup) { + fetchGroupMessagesFromService(args.groupData!.groupId); + } else { + fetchSingleMessagesFromService(); + } + + if (_canChat.not) { + showCantMessageDialog(); + } + super.onReady(); + } + + bool get isGroup => args.groupData != null; + + /// This method checks the current date if it lies within schedule time or not. + /// If it does, it means chat is enabled else chat will be disabled. + /// If schedule times not available, it returns true; + bool get _canChat { + final startMills = args.groupData?.scheduleTime.startTime ?? 0; + final endMills = args.groupData?.scheduleTime.endTime ?? 0; + + if (startMills > 0 && endMills > 0) { + // Schedule times are available + + final scheduleTime = _getScheduleTime(startMills, endMills); + final currentTime = TimeOfDay.now(); + + // Current time within start, end schedule time + return (currentTime.isAfter(scheduleTime.start) && + currentTime.isBefore(scheduleTime.end)); + } else { + // Schedule times not available + return true; + } + } + + //Make sure to pass correct millis values + ({TimeOfDay start, TimeOfDay end}) _getScheduleTime( + int startMills, int endMills) { + final sd = DateTime.fromMillisecondsSinceEpoch(startMills); + final ed = DateTime.fromMillisecondsSinceEpoch(endMills); + + final startTime = TimeOfDay(hour: sd.hour, minute: sd.minute); + final endTime = TimeOfDay(hour: ed.hour, minute: ed.minute); + + return (start: startTime, end: endTime); + } + + initializeSocket() { + debugPrint('Socket address: ${WebUrls.socketUrl}'); + + _socketIO = io( + WebUrls.socketUrl, + OptionBuilder() + .setTransports(['websocket']) // for Flutter or Dart VM + .enableForceNewConnection() + .enableAutoConnect() + .setExtraHeaders({'foo': 'bar'}) // optional + .build()); + + isSocketConnected.value = true; + _socketIO.onConnect((_) { + isSocketConnected.value = true; + debugPrint('Socket Connected'); + }); + _socketIO.onDisconnect((_) { + debugPrint('Socket Disconnected'); + // isSocketConnected.value = false; + }); + _socketIO.onConnectError((e) { + debugPrint('Socket Connection Error: ${e.toString()}'); + // isSocketConnected.value = false; + }); + _socketIO.onConnectTimeout((e) { + debugPrint('Socket timeout Error: ${e.toString()}'); + // isSocketConnected.value = false; + }); + + //listenIdReceive + listenId = (isGroup) ? args.groupData!.groupId : (myId + args.otherUserId); + debugPrint('listen on: $listenId'); + _socketIO.on(listenId, (data) { + debugPrint('listen on listenId: $data'); + _handleIncomingMessages(data); + }); + } + + _handleIncomingMessages(Map chatJson) { + log("chat listen: $chatJson"); + // _total += 1; + // _skip += 1; + final chatModel = ChatModel.fromJson(chatJson); + + if (chatJson.containsKey('createdAt')) { + chatModel.date = + DateTime.tryParse(chatJson["createdAt"])?.millisecondsSinceEpoch ?? 0; + } else { + chatModel.date = DateTime.now().millisecondsSinceEpoch; + } + + if (isGroup) { + if (chatJson.containsKey('userId') && chatJson["userId"] is Map) { + chatModel.from = UserData.fromJson(chatJson["userId"]); + } + _onFirstMessageSent(); + } else { + chatModel.from = UserData( + id: args.otherUserId, + name: args.name, + profilePictureUrl: args.profilePicPath); + chatModel.to = UserData(id: chatJson['to']); + } + messages.insert(0, chatModel); + args.onLastMessageUpdate?.call(chatModel); + } + + Future fetchSingleMessagesFromService() async { + // _skip = 0; + final response = await ChatService() + .allSingleUsersChatMessagesServerAdmin( + from: myId, + to: args.otherUserId, + ) + .showLoader(); + + if (response is List) { + if (response.isNotEmpty) { + //Note: Converting this response to List of ChatModel objects + // List chats = []; + // await Future.forEach(response.reversed, (e) { + // chats.add(ChatModel( + // id: e.id, + // from: e.from, + // to: e.to, + // message: e.message, + // date: + // DateTime.tryParse(e.createdAt)?.millisecondsSinceEpoch ?? 0)); + // }); + messages.value = response.reversed.toList(); + } + } else if (response is String) { + FrequentFunctions.showToast(message: response); + } + } + + void fetchGroupMessagesFromService(String idOfGroup) async { + dynamic response = await ChatService().allGroupMessages( + sortOrder: -1, + offset: 0, + limit: 100000, + groupId: idOfGroup, + isDeleted: false).showLoader(); + + if (response is List) { + if (response.isNotEmpty) { + //Note: Converting this response to List of ChatModel objects + List chats = []; + await Future.forEach(response, (e) { + chats.add(ChatModel( + id: e.id, + from: e.userId, + message: e.message, + messageType: e.messageType, + filePath: e.filePath, + date: + DateTime.tryParse(e.createdAt)?.millisecondsSinceEpoch ?? 0)); + }); + messages.value = chats; + } + } else if (response is String) { + FrequentFunctions.showToast(message: response); + } + } + + void sendMessageButtonPressed() async { + if (messageTEC.text.trim().isEmpty) { + return; + } + + _sendMessage( + message: messageTEC.text.trim(), + ); + messageTEC.clear(); + } + + //if sending first message, then updating chat list screen by calling it's listing api + _onFirstMessageSent() { + if (messages.length == 1) { + try { + final iController = Get.find(); + iController.onFirstMessageSend.value = true; + print("Got controller"); + } catch (e) { + debugPrint(e.toString()); + } + } + } + + _sendMessage({ + String? message, + File? file, + }) { + if (isGroup) { + _sendGroupMessage( + message: message, + file: file, + ); + } else { + _sendPrivateMessage( + message: message, + file: file, + ); + } + } + + ///Note: no need to update ui for sent message here because + ///it's gonna handled by socket event + _sendGroupMessage({ + String? message, + File? file, + }) async { + await ChatService().addGroupMessageService( + message: message ?? "", + messageType: (file != null) ? MessageType.file : MessageType.message, + file: file, + userId: myId, + groupId: args.groupData!.groupId, + ); + } + + ///Note: handling messages list update also here + _sendPrivateMessage({ + String? message, + File? file, + }) async { + final id = DateTime.now().millisecondsSinceEpoch; + + final model = ChatModel( + id: id.toString(), + from: UserData(id: LocalStorageManager.userId), + to: UserData( + id: args.otherUserId, + name: args.name, + profilePictureUrl: args.profilePicPath, + ), + message: message, + messageType: + (file != null) ? MessageType.file.name : MessageType.message.name, + fileType: (file != null) ? ChatModel.fileTypeLocalPath : null, + date: id, + state: ChatModel.stateLoading, + ); + + messages.insert(0, model); + args.onLastMessageUpdate?.call(model); + + dynamic response = await ChatService().addSingleMessage( + message: message ?? "", + messageType: (file != null) ? MessageType.file : MessageType.message, + file: file, + senderId: LocalStorageManager.userId, + receiverId: args.otherUserId, + ); + + final msg = messages.firstWhereOrNull((e) { + return e.id == id.toString(); + }); + + if (msg != null) { + final index = messages.indexOf(msg); + + if (response is SingleChatModelClass) { + //message sent successfully + msg.id = response.id; + msg.fileType = null; + msg.state = ChatModel.stateSuccess; + + messages + ..removeAt(index) + ..insert(index, msg); + + _onFirstMessageSent(); + } else { + msg.state = ChatModel.stateError; + messages.removeAt(index); + } + } + } + + pickAndSendFile() async { + Get.focusScope?.unfocus(); + Get.bottomSheet(CupertinoActionSheet( + actions: [ + ListTile( + onTap: () { + Get.back(); + ImagePickerPopup.getImageFromSource( + fromCamera: true, + onFetchImage: (f) { + _sendMessage(file: f); + }); + }, + leading: const Icon(CupertinoIcons.camera), + title: const Text("Camera"), + trailing: Icon( + Icons.arrow_forward_ios_rounded, + size: 18.r, + ), + ), + ListTile( + onTap: () async { + Get.back(); + FilePickerResult? result = + await FilePicker.platform.pickFiles(type: FileType.media); + + if (result != null && result.files.single.path != null) { + if (result.files.single.path!.isImageFileName) { + _sendMessage(file: File(result.files.single.path!)); + } else { + FrequentFunctions.showToast(message: "File doesn't supported "); + } + } + }, + leading: const Icon(CupertinoIcons.photo_on_rectangle), + title: const Text("Gallery"), + trailing: Icon( + Icons.arrow_forward_ios_rounded, + size: 18.r, + ), + ), + ListTile( + onTap: () async { + Get.back(); + final FilePickerResult? result = await FilePicker.platform + .pickFiles( + type: FileType.custom, + allowedExtensions: ["pdf", "doc", "docx", "xlsx", "xls"]); + + if (result != null) { + _sendMessage(file: File(result.files.single.path!)); + } + }, + leading: const Icon(Icons.attach_file), + title: const Text("File"), + trailing: Icon( + Icons.arrow_forward_ios_rounded, + size: 18.r, + ), + ), + ], + )); + } + + // _sendImage(File file) async { + // debugPrint("file: ${file.path}"); + // // + // // ChatModel model = ChatModel( + // // sentBy: UserData(id: myId), + // // sentTo: otherUser, + // // date: DateTime.now().toUtc().millisecondsSinceEpoch, + // // files: [file.path], + // // fileType: fileTypeLocalPath, + // // state: ChatModel.stateLoading); + // // + // // final modelHash = model.hashCode.toString(); + // // model.localId = modelHash; + // // + // // debugPrint("message modelHash: $modelHash"); + // // _handleChat(model.toJson()); + // // + // // // return; + // // var res = await repository.sendChatAttachmentApi( + // // req: ChatMessageRequest( + // // sentBy: myId, + // // sentTo: otherUser.id!, + // // files: [file], + // // fileType: "image"), + // // ); + // // + // // final i = messages.indexWhere((e) => e.localId == modelHash); + // // + // // if (res.success == true) { + // // if (i != -1) { + // // messages[i].state = ChatModel.stateSuccess; + // // messages.refresh(); + // // } + // // } else { + // // if (i != -1) { + // // messages[i].state = ChatModel.stateError; + // // messages.refresh(); + // // } + // // } + // } + + //Always call this method if _canChat is false + void showCantMessageDialog() { + final startMills = args.groupData!.scheduleTime.startTime; + final endMills = args.groupData!.scheduleTime.endTime; + + final scheduleTime = _getScheduleTime(startMills, endMills); + final sd = DateTime( + 2024, 1, 1, scheduleTime.start.hour, scheduleTime.start.minute, 0); + final ed = + DateTime(2024, 1, 1, scheduleTime.end.hour, scheduleTime.end.minute, 0); + + showDialog( + context: screenKey.currentState!.context, + builder: (BuildContext context) { + return CustomMessageDialog( + dialogButtonText: "Close", + dialogMessageText: + "It is currently outside the working hours. You can only send message during the working hours", + dialogMessageTextBold: + "Working hours: ${DateFormat("hh:mm aa").format(sd)} - ${DateFormat("hh:mm aa").format(ed)}", + headingText: "You Can't message right now", + ); + }, + ); + } + + // new + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + try { + _socketIO.clearListeners(); + _socketIO.disconnect(); + _socketIO.destroy(); + _socketIO.dispose(); + } catch (e) { + print(e); + } + messageTEC.dispose(); + messageFN.dispose(); + // scrollController.dispose(); + Get.delete(); + super.dispose(); + } +} diff --git a/lib/view/screens/chat/widgets/chat_screen_footer_widget.dart b/lib/view/screens/chat/widgets/chat_screen_footer_widget.dart new file mode 100644 index 0000000..8ff3f9e --- /dev/null +++ b/lib/view/screens/chat/widgets/chat_screen_footer_widget.dart @@ -0,0 +1,89 @@ +import 'package:flutter_svg/svg.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; + +class ChatScreenFooterWidget extends StatelessWidget { + const ChatScreenFooterWidget({ + super.key, + required this.controller, + required this.enabled, + }); + + final ChatScreenController controller; + final bool enabled; + + @override + Widget build(BuildContext context) { + return Padding( + padding: REdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Align( + alignment: Alignment.bottomCenter, + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + IconButton( + onPressed: controller.pickAndSendFile, + icon: SvgPicture.asset( + AssetsManager.svgIcAdd, + width: 32.r, + height: 32.r, + ), + ), + Expanded( + child: TextField( + minLines: 1, + maxLines: 5, + textAlign: TextAlign.left, + controller: controller.messageTEC, + keyboardType: TextInputType.multiline, + focusNode: controller.messageFN, + enabled: enabled, + decoration: InputDecoration( + hintText: 'Type Message...', + // border: InputBorder.none, + enabledBorder: OutlineInputBorder( + borderRadius: 4.toRadius(), + borderSide: BorderSide( + width: 1, + color: Colors.grey.withOpacity(0.3), + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: 4.toRadius(), + borderSide: BorderSide( + width: 1, + color: Get.theme.primaryColor, + ), + ), + contentPadding: EdgeInsets.symmetric(horizontal: 10.w), + ), + ), + ), + 5.horizontalSpace, + SizedBox( + width: 80.w, + height: 40.r, + child: IgnorePointer( + ignoring: enabled.not, + child: CustomAppButton( + buttonText: "Send", + buttonColor: + enabled ? Get.theme.primaryColor : Colors.grey, + borderColor: + enabled ? Get.theme.primaryColor : Colors.grey, + onTap: controller.sendMessageButtonPressed, + ), + )), + ], + ), + 8.verticalSpace, + ], + ), + )); + } +} diff --git a/lib/view/screens/chat/widgets/message_bubble.dart b/lib/view/screens/chat/widgets/message_bubble.dart new file mode 100644 index 0000000..20f36cd --- /dev/null +++ b/lib/view/screens/chat/widgets/message_bubble.dart @@ -0,0 +1,420 @@ +import 'dart:io'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:flutter/material.dart'; +import 'package:photo_view/photo_view.dart'; +import 'package:url_launcher/url_launcher.dart'; +import '../../../custom_widgets/my_circle_image.dart'; +import 'package:path/path.dart' as path; + +enum MessageType { sent, received } + +enum MessageSeenStatus { delivered, seen } + +enum MessageContentType { text, file, url } + +enum MessageState { + stateNone(0), + stateError(-1), + stateSending(1), + stateSuccess(2); + + final int intValue; + + static MessageState stateFromIntValue(int value) { + switch (value) { + case -1: + return MessageState.stateError; + case 1: + return MessageState.stateSending; + case 2: + return MessageState.stateSuccess; + default: + return MessageState.stateNone; + } + } + + const MessageState(this.intValue); +} + +class MessageBubble extends StatelessWidget { + final String senderName; + final String content; + final MessageState state; + final MessageContentType contentType; + final String profilePic; + final MessageType type; + final MessageSeenStatus status; + final String messageTime; + final Color? sentMessageColor; + final Color? receivedMessageColor; + final Color? sentMessageTextColor; + final Color? receivedMessageTextColor; + final bool showReportButton; + + const MessageBubble({ + Key? key, + required this.senderName, + required this.content, + required this.contentType, + required this.state, + required this.profilePic, + required this.type, + required this.status, + required this.messageTime, + this.sentMessageColor, + this.receivedMessageColor, + this.sentMessageTextColor, + this.receivedMessageTextColor, + this.showReportButton = true, + }) : super(key: key); + + Color get _backgroundColor => (type == MessageType.sent) + ? (sentMessageColor ?? Get.theme.primaryColor) + : (receivedMessageColor ?? const Color(0xffC1C1C5)); + + Color get messageColor => (type == MessageType.sent) + ? (sentMessageTextColor ?? Colors.black) + : (receivedMessageTextColor ?? Colors.white); + + // double get _paddingLeft => (type == MessageType.sent) ? 0.15.sw : 0; + + final radius = 16.0; + + @override + Widget build(BuildContext context) { + final loader = Visibility( + visible: (state == MessageState.stateSending), + child: Align( + alignment: Alignment.topCenter, + child: SizedBox.square( + dimension: 18, + child: CircularProgressIndicator( + color: Get.theme.primaryColor, + strokeWidth: 2, + )), + ), + ); + + final messageBox = SizedBox( + width: 0.7.sw, + child: Padding( + padding: REdgeInsets.symmetric(vertical: 5), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (type == MessageType.received) + ? MyCircleImage( + imageSize: 20.r, + url: WebUrls.baseUrl + profilePic, + errorWidget: const DecoratedBox( + decoration: BoxDecoration(color: Color(0xffC1C1C5)), + child: Center( + child: Icon( + Icons.image_not_supported_outlined, + color: Colors.black, + size: 12, + ), + ), + ), + ).paddingOnly(right: 8.r) + : FrequentFunctions.noWidget, + Expanded( + child: (contentType == MessageContentType.text) + ? _textMessage() + : (content.isImageFileName) + ? _imageWidget() + : _documentFileWidget(), + ), + ], + ), + 4.verticalSpace, + Row( + children: [ + (type == MessageType.received) + ? SizedBox(width: 28.r) + : FrequentFunctions.noWidget, + (state == MessageState.stateSending) + ? loader + : (state == MessageState.stateError) + ? Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.warning_amber_rounded, + color: Colors.red, + size: 14.r, + ), + 4.horizontalSpace, + Text( + 'Message not sent', + style: const TextStyle().copyWith( + color: Colors.red, + ), + ) + ], + ) + : FrequentFunctions.noWidget + ], + ) + ], + ), + ), + ); + + return SizedBox( + width: double.maxFinite, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: (type == MessageType.sent) + ? MainAxisAlignment.end + : MainAxisAlignment.start, + children: [messageBox], + ), + ); + } + + Widget _textMessage() => Container( + padding: REdgeInsets.symmetric(horizontal: 14, vertical: 12), + decoration: + BoxDecoration(color: _backgroundColor, borderRadius: 10.toRadius()), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + (type == MessageType.received) + ? CustomTextWidget( + text: senderName, + fontColor: messageColor, + textAlign: TextAlign.left, + fontSize: 12.sp, + fontWeight: FontWeight.w600, + ).paddingOnly(bottom: 10.r) + : FrequentFunctions.noWidget, + CustomTextWidget( + text: content, + fontColor: messageColor, + textAlign: TextAlign.left, + fontSize: 12.sp, + fontWeight: FontWeight.w400, + ), + 4.verticalSpace, + Align( + alignment: Alignment.centerRight, + child: _messageTimeWidget(), + ) + ], + ), + ); + + Widget _imageWidget() { + return InkWell( + onTap: _openPreviewDialog, + child: Container( + width: 200.r, + height: 250.r, + clipBehavior: Clip.antiAlias, + padding: REdgeInsets.all(4), + decoration: BoxDecoration( + color: _backgroundColor, + borderRadius: radius.toRadius()), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + //Sender Name + (type == MessageType.received) + ? CustomTextWidget( + text: senderName, + fontColor: messageColor, + textAlign: TextAlign.left, + fontSize: 12.sp, + fontWeight: FontWeight.w600, + ).paddingOnly(bottom: 10.r) + : FrequentFunctions.noWidget, + + //image + Expanded( + child: ClipRRect( + clipBehavior: Clip.antiAlias, + borderRadius: (radius - 4).toRadius(), + child: (contentType == MessageContentType.file) + ? Image.file(File(content), fit: BoxFit.cover) + : CachedNetworkImage( + imageUrl: (WebUrls.baseUrl + content), + fit: BoxFit.cover, + ), + ), + ), + + 8.verticalSpace, + + //Time + Align( + alignment: Alignment.centerRight, + child: _messageTimeWidget(), + ).addPaddingHorizontal(12), + 8.verticalSpace, + ], + ), + ), + ); + } + + Widget _documentFileWidget() { + return Container( + decoration: BoxDecoration( + color: _backgroundColor, + borderRadius: radius.toRadius(), + ), + clipBehavior: Clip.antiAlias, + padding: REdgeInsets.all(4), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + //Sender Name + (type == MessageType.received) + ? CustomTextWidget( + text: senderName, + fontColor: messageColor, + textAlign: TextAlign.left, + fontSize: 12.sp, + fontWeight: FontWeight.w600, + ).paddingOnly(bottom: 10.r) + : FrequentFunctions.noWidget, + + //File + InkWell( + onTap: () { + if (contentType == MessageContentType.url) { + _launchUrl(WebUrls.baseUrl + content); + } else { + _launchUrl(content); + } + }, + child: Card( + elevation: 0, + surfaceTintColor: Colors.white, + child: Padding( + padding: REdgeInsets.symmetric(horizontal: 12, vertical: 12), + child: Row( + children: [ + CircleAvatar( + radius: 24.r, + backgroundColor: + CustomAppColors.kSecondaryColor.withOpacity(0.1), + child: const Icon( + Icons.file_copy_outlined, + color: CustomAppColors.kSecondaryColor, + ), + ), + 12.horizontalSpace, + Expanded( + child: CustomTextWidget( + text: path.basename(content), + fontColor: messageColor, + textAlign: TextAlign.left, + fontSize: 12.sp, + fontWeight: FontWeight.w400, + ), + ) + ], + ), + ), + ), + ), + 4.verticalSpace, + + //Time + Align( + alignment: Alignment.centerRight, + child: _messageTimeWidget(), + ).addPaddingHorizontal(12), + ], + ), + ); + } + + Widget _messageTimeWidget() { + return (messageTime.isNotEmpty) + ? Text( + DateFormat("hh:mm aa") + .format(DateTime.parse(messageTime).toLocal()), + style: TextStyle( + color: (type == MessageType.sent) ? Colors.grey : Colors.white, + fontSize: 10.sp, + fontWeight: FontWeight.w400, + ), + ) + : FrequentFunctions.noWidget; + } + + void _openPreviewDialog() { + final img = (contentType == MessageContentType.file) + ? FileImage(File(content)) + : CachedNetworkImageProvider(WebUrls.baseUrl + content); + Get.dialog( + Material( + type: MaterialType.transparency, + child: SizedBox( + width: double.maxFinite, + height: double.maxFinite, + child: Stack( + fit: StackFit.expand, + children: [ + Positioned.fill( + child: PhotoViewGestureDetectorScope( + axis: Axis.vertical, + child: PhotoView( + tightMode: true, + backgroundDecoration: const BoxDecoration( + color: Colors.transparent, + ), + minScale: PhotoViewComputedScale.contained, + maxScale: PhotoViewComputedScale.covered * 1.1, + initialScale: PhotoViewComputedScale.contained, + imageProvider: img as ImageProvider, + heroAttributes: + const PhotoViewHeroAttributes(tag: "someTag"), + ), + ), + ), + Positioned( + right: 12, + top: 12, + child: InkWell( + onTap: Get.back, + child: Card( + color: Colors.white, + shape: 24.toRoundedRectRadius(), + elevation: 4, + child: RSizedBox.square( + dimension: 40, + child: Icon( + Icons.close, + color: Colors.black, + size: 24.r, + ), + ), + ), + ), + ) + ], + ), + ), + ), + ); + } + + Future _launchUrl(String url) async { + try { + await launchUrl(Uri.parse(url)); + } catch (e) { + debugPrint(e.toString()); + } + } +} diff --git a/lib/view/screens/clients/addEditMemoryBox/add_edit_memory_box_screen.dart b/lib/view/screens/clients/addEditMemoryBox/add_edit_memory_box_screen.dart new file mode 100644 index 0000000..4d73cb9 --- /dev/null +++ b/lib/view/screens/clients/addEditMemoryBox/add_edit_memory_box_screen.dart @@ -0,0 +1,203 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/memoryListResponse/MemoryListData.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/my_network_image.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'controller/add_edit_memory_box_screen_controller.dart'; +import 'widget/VideoPlayerWidget.dart'; + +class AddEditMemoryBoxScreenArgs { + final UserData userData; + final MemoryListData? data; + final bool viewOnly; + + AddEditMemoryBoxScreenArgs({ + required this.userData, + this.data, + this.viewOnly = false, + }); +} + +//This screen is used to view, add and edit memory box +class AddEditMemoryBoxScreen extends StatefulWidget { + final AddEditMemoryBoxScreenArgs args; + + const AddEditMemoryBoxScreen({super.key, required this.args}); + + @override + State createState() => _AddEditMemoryBoxScreenState(); +} + +class _AddEditMemoryBoxScreenState extends State { + final controller = Get.put(AddEditMemoryBoxScreenController()); + + @override + void initState() { + controller.serviceUserId = widget.args.userData.id!; + + if (forUpdate) { + controller.filePath.value = + '${WebUrls.baseUrl}${widget.args.data!.filePath ?? ""}'; + controller.noteDetailsController.text = widget.args.data!.note ?? ""; + } + super.initState(); + } + + bool get forUpdate => widget.args.data != null; + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: + "${widget.args.viewOnly ? "View" : forUpdate ? "Update" : "Add"} Memory Box", + ), + body: SafeArea( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 20.r), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: (widget.args.viewOnly) + ? _viewOnlyWidgets + : [ + 12.verticalSpace, + Obx(() { + final path = controller.filePath(); + return mediaViewer(path: path); + }), + CustomTextWidget( + text: "Upload File", + textAlign: TextAlign.left, + fontSize: 16.sp, + fontColor: Colors.black, + fontWeight: FontWeight.w600, + ), + // CustomTextWidget( + // text: "Please upload a file that is 1MB or smaller.", + // textAlign: TextAlign.left, + // fontSize: 12.sp, + // fontColor: Colors.grey, + // ), + 12.verticalSpace, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + flex: 1, + child: SizedBox( + height: 32.r, + child: CustomAppButton( + buttonColor: Get.theme.primaryColor, + buttonText: "Choose File", + onTap: controller.onFileChooseButtonTap, + ), + ).paddingOnly(right: 8.r), + ), + Expanded( + flex: 2, + child: Obx(() { + return controller.filePath.isNotEmpty + ? Obx( + () => CustomTextWidget( + text: controller.filePath.value + .split("/") + .last, + isExpanded: false, + textAlign: TextAlign.left, + ), + ) + : FrequentFunctions.noWidget; + }), + ), + ], + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + controller: controller.noteDetailsController, + heading: "Note Details", + hintText: "Type here...", + minLines: 6, + maxLines: 6, + onChange: (_) {}, + ), + 32.verticalSpace, + CustomAppButton( + buttonText: forUpdate ? "Update" : "Save", + onTap: () { + forUpdate + ? controller + .updateButtonPress(widget.args.data!.id!) + : controller.saveButtonPress(); + }, + ), + 5.verticalSpace, + ], + ), + ), + ), + ), + ); + } + + List get _viewOnlyWidgets { + return [ + 12.verticalSpace, + mediaViewer( + path: '${WebUrls.baseUrl}${widget.args.data?.filePath ?? ""}'), + 20.verticalSpace, + Column( + children: [ + CustomTextWidget( + text: "Note Details", + textAlign: TextAlign.left, + fontSize: 16.sp, + fontWeight: FontWeight.w600, + fontColor: Colors.black, + ), + CustomTextWidget( + text: widget.args.data?.note ?? "", + textAlign: TextAlign.left, + fontSize: 14.sp, + fontColor: Colors.black, + ), + ], + ) + ]; + } + + Widget mediaViewer({required String path}) { + if (path.isVideoFileName) { + return VideoPlayerWidget( + path: path, + type: path.startsWith("http") + ? VideoSourceType.network + : VideoSourceType.file, + ); + } else if (path.isImageFileName) { + if (path.startsWith("http")) { + return MyNetworkImage(url: path); + } else { + return Image.file(File(path)); + } + } else { + return FrequentFunctions.noWidget; + } + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/addEditMemoryBox/controller/add_edit_memory_box_screen_controller.dart b/lib/view/screens/clients/addEditMemoryBox/controller/add_edit_memory_box_screen_controller.dart new file mode 100644 index 0000000..14d3270 --- /dev/null +++ b/lib/view/screens/clients/addEditMemoryBox/controller/add_edit_memory_box_screen_controller.dart @@ -0,0 +1,98 @@ +import 'dart:io'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/utilities/frequent_functions.dart'; +import 'package:ftc_mobile_app/web_services/client_services.dart'; +import 'package:get/get.dart'; + +class AddEditMemoryBoxScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + TextEditingController documentTitleController = TextEditingController(); + TextEditingController noteDetailsController = TextEditingController(); + + late final String serviceUserId; + + final file = Rx(null); + + //Should stores full path of file or url + final filePath = "".obs; + + onFileChooseButtonTap() async { + FilePickerResult? result = + await FilePicker.platform.pickFiles(type: FileType.media); + + if (result != null && result.files.single.path != null) { + if (result.files.single.path!.isVideoFileName || + result.files.single.path!.isImageFileName) { + file.value = File(result.files.single.path!); + filePath.value = result.files.single.path!; + } else { + FrequentFunctions.showToast(message: "Unsupported File"); + } + } + } + + Future saveButtonPress() async { + if (file() == null) { + FrequentFunctions.showToast(message: "Please select file first"); + return; + } + if (noteDetailsController.text.trim().isEmpty) { + FrequentFunctions.showToast(message: "Note details field is required"); + return; + } + + var response = await ClientService() + .addMemoryBoxFile( + userId: serviceUserId, + filePath: file.value!.path, + noteDetails: noteDetailsController.text, + ) + .showLoader(); + + if (response == true) { + backButtonPressed(argument: true); + } else { + FrequentFunctions.showToast(message: response); + } + } + + Future updateButtonPress(String id) async { + if (noteDetailsController.text.trim().isEmpty) { + FrequentFunctions.showToast(message: "Note details field is required"); + return; + } + + var response = await ClientService() + .updateMemoryBoxFile( + id: id, + userId: serviceUserId, + filePath: file.value?.path ?? "", + noteDetails: noteDetailsController.text, + ) + .showLoader(); + + if (response == true) { + backButtonPressed(argument: true); + } else { + FrequentFunctions.showToast(message: response); + } + } + + void backButtonPressed({dynamic argument}) { + Navigator.of(screenKey.currentContext!).pop(argument); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + noteDetailsController.dispose(); + Get.delete(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/addEditMemoryBox/widget/VideoPlayerWidget.dart b/lib/view/screens/clients/addEditMemoryBox/widget/VideoPlayerWidget.dart new file mode 100644 index 0000000..232691f --- /dev/null +++ b/lib/view/screens/clients/addEditMemoryBox/widget/VideoPlayerWidget.dart @@ -0,0 +1,93 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:video_player/video_player.dart'; + +enum VideoSourceType { file, network } + +class VideoPlayerWidget extends StatefulWidget { + final String path; + final VideoSourceType type; + + const VideoPlayerWidget({ + super.key, + required this.path, + required this.type, + }); + + @override + State createState() => _VideoPlayerWidgetState(); +} + +class _VideoPlayerWidgetState extends State { + VideoPlayerController? _videoPlayerController; + final RxBool isInitialized = false.obs; + final RxBool isError = false.obs; + final RxBool isPlaying = false.obs; + double aspectRatio = 16.0 / 9.0; + + @override + void initState() { + print("Video path: ${widget.path}"); + if (widget.type == VideoSourceType.network) { + print("VideoSourceType.network"); + _videoPlayerController = + VideoPlayerController.networkUrl(Uri.parse(widget.path)); + } else { + print("VideoSourceType.file"); + _videoPlayerController = VideoPlayerController.file(File(widget.path)); + } + + _videoPlayerController?.initialize().then((_) { + isInitialized.value = true; + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AspectRatio( + aspectRatio: aspectRatio, + child: Obx(() { + return (isError()) + ? FrequentFunctions.centerText(text: "Something went wrong") + : isInitialized.isFalse + ? FrequentFunctions.centerText(text: "Loading video...") + : Stack( + children: [ + Positioned.fill( + child: VideoPlayer(_videoPlayerController!), + ), + Center( + child: Obx(() { + return (isPlaying()) + ? FrequentFunctions.noWidget + : InkWell( + onTap: () { + if (isPlaying.isFalse) { + isPlaying.value = true; + _videoPlayerController?.play(); + } + }, + child: Icon( + Icons.play_circle, + color: Get.theme.primaryColor, + size: 56.r, + ), + ); + }), + ) + ], + ); + }), + ); + } + + @override + void dispose() { + _videoPlayerController?.dispose(); + _videoPlayerController = null; + super.dispose(); + } +} diff --git a/lib/view/screens/clients/addEditRiskAssessment/add_edit_risk_assessment_screen.dart b/lib/view/screens/clients/addEditRiskAssessment/add_edit_risk_assessment_screen.dart new file mode 100644 index 0000000..9b63b21 --- /dev/null +++ b/lib/view/screens/clients/addEditRiskAssessment/add_edit_risk_assessment_screen.dart @@ -0,0 +1,358 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/riskAssessmentResponse/RiskAssessmentData.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import 'controller/add_edit_risk_assessment_screen_controller.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class AddEditRiskAssessmentScreenArgs { + final UserData userData; + + //If [issueData] is not null, then this screen will update its content instead + final RiskAssessmentData? riskAssessmentData; + + AddEditRiskAssessmentScreenArgs({ + required this.userData, + this.riskAssessmentData, + }); +} + +class AddEditRiskAssessmentScreen extends StatefulWidget { + final AddEditRiskAssessmentScreenArgs args; + + const AddEditRiskAssessmentScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => + _AddDetailsToNewBodyPointScreenState(); +} + +class _AddDetailsToNewBodyPointScreenState + extends State { + final AddEditRiskAssessmentScreenController controller = + Get.put(AddEditRiskAssessmentScreenController()); + + @override + void initState() { + //Note: Important + controller.serviceUserId = widget.args.userData.id!; + + if (widget.args.riskAssessmentData != null) { + controller.isEditing.value = true; + + controller.hazardTEC.text = widget.args.riskAssessmentData!.hazard ?? ""; + controller.personsExposedToHazardTEC.text = + widget.args.riskAssessmentData!.personsExposedToHazard ?? ""; + controller.riskIdentifiedTEC.text = + widget.args.riskAssessmentData!.riskIdentified ?? ""; + controller.controlMeasuresRequiredTEC.text = + widget.args.riskAssessmentData!.coldMeasureRequired ?? ""; + + //Pure Risk Rating + controller.pureRiskRatingCTEC.text = + widget.args.riskAssessmentData!.pureRiskRating?.c?.toString() ?? ""; + controller.pureRiskRatingLTEC.text = + widget.args.riskAssessmentData!.pureRiskRating?.l?.toString() ?? ""; + controller.pureRiskRatingRTEC.text = + widget.args.riskAssessmentData!.pureRiskRating?.r?.toString() ?? ""; + + //In Place Rating + controller.inPlaceYTEC.text = + widget.args.riskAssessmentData!.inPlace?.y?.toString() ?? ""; + controller.inPlaceNTEC.text = + widget.args.riskAssessmentData!.inPlace?.n?.toString() ?? ""; + + //Residual Risk Rating + controller.residualRiskRatingCTEC.text = + widget.args.riskAssessmentData!.residualRiskRating?.c?.toString() ?? + ""; + controller.residualRiskRatingLTEC.text = + widget.args.riskAssessmentData!.residualRiskRating?.l?.toString() ?? + ""; + controller.residualRiskRatingRTEC.text = + widget.args.riskAssessmentData!.residualRiskRating?.r?.toString() ?? + ""; + } + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Obx(() { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly(context, + titleText: controller.isEditing() + ? "Update Risk Assessment" + : "Add New Risk Assessment"), + body: SafeArea( + child: ListView( + padding: EdgeInsets.symmetric(horizontal: 16.r), + children: [ + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.hazardTEC, + textCapitalization: TextCapitalization.sentences, + heading: "Hazard", + hintText: "", + ), + 20.verticalSpace, + headingText("Pure Risk Rating"), + 8.verticalSpace, + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text("C"), + 4.verticalSpace, + dropdown( + textEditingController: + controller.pureRiskRatingCTEC), + ], + ), + ), + 8.horizontalSpace, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text("L"), + 4.verticalSpace, + dropdown( + textEditingController: + controller.pureRiskRatingLTEC), + ], + ), + ), + 8.horizontalSpace, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text("R"), + 4.verticalSpace, + dropdown( + textEditingController: + controller.pureRiskRatingRTEC), + ], + ), + ), + ], + ), + 20.verticalSpace, + headingText("In Place"), + 8.verticalSpace, + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text("Y"), + 4.verticalSpace, + dropdown(textEditingController: controller.inPlaceYTEC), + ], + ), + ), + 8.horizontalSpace, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text("N"), + 4.verticalSpace, + dropdown(textEditingController: controller.inPlaceNTEC), + ], + ), + ), + ], + ), + 20.verticalSpace, + headingText("Residual Risk Rating"), + 8.verticalSpace, + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text("C"), + 4.verticalSpace, + dropdown( + textEditingController: + controller.residualRiskRatingCTEC), + ], + ), + ), + 8.horizontalSpace, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text("L"), + 4.verticalSpace, + dropdown( + textEditingController: + controller.residualRiskRatingLTEC), + ], + ), + ), + 8.horizontalSpace, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text("R"), + 4.verticalSpace, + dropdown( + textEditingController: + controller.residualRiskRatingRTEC), + ], + ), + ), + ], + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.personsExposedToHazardTEC, + inputType: TextInputType.multiline, + textCapitalization: TextCapitalization.sentences, + heading: "Personal (s) Exposed to hazard", + hintText: "", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.riskIdentifiedTEC, + inputType: TextInputType.multiline, + textCapitalization: TextCapitalization.sentences, + heading: "Risk Identified", + hintText: "", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.controlMeasuresRequiredTEC, + inputType: TextInputType.multiline, + textCapitalization: TextCapitalization.sentences, + heading: "Control Measures Required", + hintText: "", + onChange: (_) {}, + ), + 20.verticalSpace, + Obx( + () => CustomAppButton( + buttonText: controller.isEditing() ? "Update" : "Save", + onTap: () => controller.submitButtonPressed(context), + ), + ), + ], + ), + ), + ); + }); + } + + Widget headingText(String text) => CustomTextWidget( + text: text, + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w600, + textAlign: TextAlign.left, + ); + + Widget dropdown({ + required TextEditingController textEditingController, + }) { + return SizedBox( + height: 48.r, + child: DropdownButtonFormField( + value: textEditingController.text, + isDense: true, + onTap: () { + FocusScopeNode().unfocus(); + }, + dropdownColor: Colors.white, + decoration: InputDecoration( + contentPadding: REdgeInsets.symmetric(horizontal: 12), + border: OutlineInputBorder( + borderSide: BorderSide( + color: CustomAppColors.kLightGreyColor.withOpacity(0.2)), + borderRadius: 8.toRadius(), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: CustomAppColors.kLightGreyColor), + borderRadius: 8.toRadius(), + ), + ), + hint: Text( + "Select...", + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 14.sp, + color: CustomAppColors.kLightTextColor, + ), + ), + items: controller.ratings + .map( + (e) => DropdownMenuItem( + value: e.toString(), + child: Text(e.toString()), + ), + ) + .toList(), + isExpanded: true, + iconSize: 20.h, + icon: Padding( + padding: REdgeInsets.only(right: 4.0), + child: const Icon(Icons.arrow_drop_down_sharp, color: Colors.grey), + ), + onChanged: (v) { + textEditingController.text = v ?? "1"; + }, + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/addEditRiskAssessment/controller/add_edit_risk_assessment_screen_controller.dart b/lib/view/screens/clients/addEditRiskAssessment/controller/add_edit_risk_assessment_screen_controller.dart new file mode 100644 index 0000000..7303169 --- /dev/null +++ b/lib/view/screens/clients/addEditRiskAssessment/controller/add_edit_risk_assessment_screen_controller.dart @@ -0,0 +1,141 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/riskAssessmentResponse/InPlace.dart'; +import 'package:ftc_mobile_app/models/clients/riskAssessmentResponse/PureRiskRating.dart'; +import 'package:ftc_mobile_app/models/clients/riskAssessmentResponse/ResidualRiskRating.dart'; +import 'package:ftc_mobile_app/models/clients/riskAssessmentResponse/RiskAssessmentData.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; + +class AddEditRiskAssessmentScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final ratings = List.generate(9, (i) => i + 1); + + final hazardTEC = TextEditingController(); + final personsExposedToHazardTEC = TextEditingController(); + final riskIdentifiedTEC = TextEditingController(); + final controlMeasuresRequiredTEC = TextEditingController(); + + final pureRiskRatingCTEC = TextEditingController(text: "1"); + final pureRiskRatingLTEC = TextEditingController(text: "1"); + final pureRiskRatingRTEC = TextEditingController(text: "1"); + + final residualRiskRatingCTEC = TextEditingController(text: "1"); + final residualRiskRatingLTEC = TextEditingController(text: "1"); + final residualRiskRatingRTEC = TextEditingController(text: "1"); + + final inPlaceYTEC = TextEditingController(text: "1"); + final inPlaceNTEC = TextEditingController(text: "1"); + + ///[isEditing] will be true if using [AddDetailsToNewBodyPointScreen] screen to edit issue details, + /// or say issueData is not null + final isEditing = false.obs; + + String serviceUserId = ""; + RiskAssessmentData? issueData; + + @override + void onReady() { + if (issueData != null) { + isEditing.value = true; + hazardTEC.text = issueData!.hazard ?? ""; + personsExposedToHazardTEC.text = issueData!.personsExposedToHazard ?? ""; + } + super.onReady(); + } + + Future submitButtonPressed(BuildContext context) async { + if (hazardTEC.text.trim().isEmpty) { + FrequentFunctions.showToast(message: "Hazard is required"); + return; + } + if (personsExposedToHazardTEC.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Personal (s) Exposed to hazard is required"); + return; + } + if (riskIdentifiedTEC.text.trim().isEmpty) { + FrequentFunctions.showToast(message: "Risk Identified is required"); + return; + } + if (controlMeasuresRequiredTEC.text.trim().isEmpty) { + FrequentFunctions.showToast( + message: "Control Measures Required is required"); + return; + } + + // var result = (isEditing.isFalse) + final result = await ClientService() + .createRiskAssesments( + data: RiskAssessmentData( + userId: serviceUserId, + hazard: hazardTEC.text.trim(), + personsExposedToHazard: personsExposedToHazardTEC.text.trim(), + riskIdentified: riskIdentifiedTEC.text.trim(), + coldMeasureRequired: controlMeasuresRequiredTEC.text.trim(), + pureRiskRating: PureRiskRating( + c: int.parse(pureRiskRatingCTEC.text.trim()), + l: int.parse(pureRiskRatingLTEC.text.trim()), + r: int.parse(pureRiskRatingRTEC.text.trim()), + ), + residualRiskRating: ResidualRiskRating( + c: int.parse(residualRiskRatingCTEC.text.trim()), + l: int.parse(residualRiskRatingLTEC.text.trim()), + r: int.parse(residualRiskRatingRTEC.text.trim()), + ), + inPlace: InPlace( + y: int.parse(inPlaceYTEC.text.trim()), + n: int.parse(inPlaceNTEC.text.trim()), + ), + ), + ) + .showLoader(); + if (result == true) { + Navigator.of(screenKey.currentContext!).pop(true); + } else { + FrequentFunctions.showToast(message: result); + } + // : await ClientService() + // .updateHealthIssueData( + // issueId: issueData!.id, + // categoryId: issueData!.bodyPointsCategory!.id, + // healthNote: healthNoteController.text.trim(), + // complaint: complaintController.text.trim()) + // .showLoader(); + // + // if (result is! String) { + // Navigator.of(context).pop(true); + // } else { + // if (result.isNotEmpty) { + // FrequentFunctions.showToast(message: result); + // } + // } + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + hazardTEC.dispose(); + personsExposedToHazardTEC.dispose(); + riskIdentifiedTEC.dispose(); + controlMeasuresRequiredTEC.dispose(); + + pureRiskRatingCTEC.dispose(); + pureRiskRatingLTEC.dispose(); + pureRiskRatingRTEC.dispose(); + + residualRiskRatingCTEC.dispose(); + residualRiskRatingLTEC.dispose(); + residualRiskRatingRTEC.dispose(); + + inPlaceYTEC.dispose(); + inPlaceNTEC.dispose(); + + Get.delete(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/add_details_to_new_body_point_screen.dart b/lib/view/screens/clients/add_details_to_new_body_point_screen.dart new file mode 100644 index 0000000..77359c8 --- /dev/null +++ b/lib/view/screens/clients/add_details_to_new_body_point_screen.dart @@ -0,0 +1,110 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/HealthIssuesDetailsModel.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:get/get.dart'; +import '../../../controllers/clients/add_details_to_new_body_point_screen_controller.dart'; +import '../../../ftc_mobile_app.dart'; +import '../../custom_widgets/clients/category_subcategory_dropdowns_widget.dart'; + +class AddDetailsToNewBodyPointScreenArgs { + final UserData userData; + + //If [issueData] is not null, then this screen will update its content instead + final HealthIssueDetailsModel? issueData; + + AddDetailsToNewBodyPointScreenArgs({ + required this.userData, + this.issueData, + }); +} + +class AddDetailsToNewBodyPointScreen extends StatefulWidget { + final AddDetailsToNewBodyPointScreenArgs args; + + const AddDetailsToNewBodyPointScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => + _AddDetailsToNewBodyPointScreenState(); +} + +class _AddDetailsToNewBodyPointScreenState + extends State { + final AddDetailsToNewBodyPointScreenController controller = + Get.put(AddDetailsToNewBodyPointScreenController()); + + @override + void initState() { + controller.serviceUserId = widget.args.userData.id!; + controller.issueData = widget.args.issueData; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Obx(() { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly(context, + titleText: controller.isEditing() + ? "Update Health Issue - ${widget.args.issueData!.bodyPointsCategory?.name}" + : "Add Health Issue"), + body: SafeArea( + child: ListView( + padding: EdgeInsets.symmetric(horizontal: 16.r), + children: [ + controller.isEditing() + ? FrequentFunctions.noWidget + : CategorySubcategoryDropdownsWidget( + controller: controller.catSubCatController), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.healthNoteController, + textCapitalization: TextCapitalization.sentences, + heading: "Health Note", + hintText: "Example: Eye Pain / Headache", + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.complaintController, + inputType: TextInputType.multiline, + textCapitalization: TextCapitalization.sentences, + heading: "Complaint", + hintText: "Example: Minor Pain on right Side", + onChange: (_) {}, + ), + 20.verticalSpace, + Obx( + () => CustomAppButton( + buttonText: + controller.isEditing() ? "Update Issue" : "Add Issue", + onTap: () => controller.submitButtonPressed(context), + ), + ), + ], + ), + ), + ); + }); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/add_new_document_screen.dart b/lib/view/screens/clients/add_new_document_screen.dart new file mode 100644 index 0000000..474dad3 --- /dev/null +++ b/lib/view/screens/clients/add_new_document_screen.dart @@ -0,0 +1,183 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../../controllers/clients/add_new_document_screen_controller.dart'; +import '../../../ftc_mobile_app.dart'; + +class AddNewDocumentScreen extends StatefulWidget { + const AddNewDocumentScreen({Key? key}) : super(key: key); + + @override + State createState() => _AddNewDocumentScreenState(); +} + +class _AddNewDocumentScreenState extends State { + final controller = Get.put(AddNewDocumentScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + // onBackButton: () => controller.backButtonPressed(), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: controller.docIdReceived.value + ? " Update Document" + : controller.viewOnly.value + ? " View Document" + : " Add Document", + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + controller: controller.documentTitleController, + hintText: "Document title", + heading: "Title", + onChange: (_) {}, + ), + // ConsentScreenTextField( + // headingText: "Document title", + // hintText: "Title of Document", + // viewOnly: controller.viewOnly.value, + // fieldController: controller.documentTitleController), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + controller: controller.documentDetailsController, + heading: "Document Details", + hintText: "Details Of Document", + minLines: 6, + maxLines: 6, + onChange: (_) {}, + ), + // ConsentScreenTextField( + // headingText: "Document Details", + // hintText: "Details Of Document", + // viewOnly: controller.viewOnly.isTrue, + // fieldController: controller.documentDetailsController), + 20.verticalSpace, + + // Text( + // "Upload document", + // style: TextStyle( + // fontSize: 14.sp, + // fontWeight: FontWeight.w400, + // color: CustomAppColors.kDarkBlueTextColor, + // ), + // ), + // 6.verticalSpace, + + // Obx(() { + // + // }), + + // SizedBox( + // width: 150.r, + // height: 32.r, + // child: const CustomAppButton(buttonText: "Choose file"), + // ), + + controller.viewOnly.value + ? controller.docFilePath.isNotEmpty + ? Row( + children: [ + SizedBox( + width: MediaQuery.of(context).size.width / 3, + child: CustomTextWidget( + text: "Document:", + fontSize: 20.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + isExpanded: false, + ), + ), + SizedBox( + width: MediaQuery.of(context).size.width / 2, + child: Obx( + () => CustomTextWidget( + text: controller.docFilePath.value + .split("/") + .last, + isExpanded: false, + ), + ), + ), + ], + ) + : Container( + width: MediaQuery.of(context).size.width / 2, + ) + : Obx( + () => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + controller.docFilePath.isNotEmpty + ? Expanded( + flex: 1, + child: SizedBox( + width: + MediaQuery.of(context).size.width / 2, + child: Obx( + () => CustomTextWidget( + text: controller.docFilePath.value + .split("/") + .last, + isExpanded: false, + ), + ), + ), + ) + : Container( + width: MediaQuery.of(context).size.width / 2, + ), + Expanded( + flex: 1, + child: CustomAppButton( + buttonColor: CustomAppColors.kIconColor, + borderColor: CustomAppColors.kIconColor, + buttonText: controller.docIdReceived.value + ? "Change Document File" + : "Add Document File", + onTap: controller.onFileChooseButtonTap, + ), + ), + ], + ), + ), + 32.verticalSpace, + Obx( + () => CustomAppButton( + buttonText: controller.docIdReceived.value + ? "Update Document" + : controller.viewOnly.value + ? "Ok" + : "Add Document", + onTap: () => controller.submitButtonPressed(), + ), + ), + 5.verticalSpace, + ], + ), + ), + ), + ); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/add_new_recent_incident_screen.dart b/lib/view/screens/clients/add_new_recent_incident_screen.dart new file mode 100644 index 0000000..5ebaf19 --- /dev/null +++ b/lib/view/screens/clients/add_new_recent_incident_screen.dart @@ -0,0 +1,237 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/add_new_recent_incident_screen_controller.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/recent_incidents_model.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import 'package:quill_html_editor/quill_html_editor.dart'; + +class AddNewRecentIncidentsScreenArgs { + final String? userId; + final RecentIncidentsModel? incidentsModel; + + AddNewRecentIncidentsScreenArgs({this.userId, this.incidentsModel}); +} + +class AddNewRecentIncidentsScreen extends StatefulWidget { + final AddNewRecentIncidentsScreenArgs args; + + const AddNewRecentIncidentsScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => + _AddNewRecentIncidentsScreenState(); +} + +class _AddNewRecentIncidentsScreenState + extends State { + late final AddNewRecentIncidentScreenController controller; + + @override + void initState() { + controller = Get.put(AddNewRecentIncidentScreenController(widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + onBackButton: () => controller.onBackPress(context), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + controller.onBackPress(context); + // Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: widget.args.userId.isNotNullOrEmpty() + ? 'Add Recent Incident' + : 'Update Recent Incident', + // text: 'Add New Recent Incidents', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: SafeArea( + child: Padding( + padding: REdgeInsets.symmetric(horizontal: 20.0), + child: Column( + children: [ + Column( + children: [ + // const CustomTextWidget( + // text: "Select Incident Date", + // textAlign: TextAlign.left, + // fontSize: 14, + // fontWeight: FontWeight.w500, + // ), + + InkWell( + onTap: () => controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.incidentDateTEC, + hintText: "Select Incident Date", + heading: "Select Incident Date", + isEnabled: false, + onChange: (_) {}, + ), + ), + + // InkWell( + // onTap: () => controller.selectDateFromPicker( + // context: context, + // maxDate: DateTime(DateTime.now().year + 1), + // minDate: DateTime(DateTime.now().year - 4)), + // child: Row( + // children: [ + // Obx( + // () => CustomTextWidget( + // text: DateFormatter() + // .getHolidayDate(controller.selectedDate.value), + // isExpanded: false, + // fontWeight: FontWeight.w500, + // fontColor: CustomAppColors.kSecondaryColor, + // ), + // ), + // const Spacer(), + // const Icon(Icons.calendar_today_outlined), + // ], + // ), + // ), + ], + ), + // Container( + // padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 5.h), + // child: CustomTextFieldWidget( + // controller: controller.incidentTitleTEC, + // heading: "Incident Title"), + // ), + + 12.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.incidentTitleTEC, + textCapitalization: TextCapitalization.words, + hintText: "Incident Title", + heading: "Incident Title", + onChange: (_) {}, + ), + + 12.verticalSpace, + + ToolBar( + toolBarColor: Colors.cyan.shade50, + activeIconColor: Colors.green, + padding: const EdgeInsets.all(8), + iconSize: 20, + toolBarConfig: ToolBarStyle.values + .where((e) => [ + ToolBarStyle.image, + ToolBarStyle.video, + ].contains(e).not) + .toList(), + controller: controller.incidentDetailsQuillFieldController, + ), + + Expanded( + child: LayoutBuilder(builder: (_, c) { + return QuillHtmlEditor( + hintText: 'Recent Incident Details', + controller: controller.incidentDetailsQuillFieldController, + isEnabled: true, + minHeight: c.maxHeight, + text: widget.args.incidentsModel?.note ?? "", + hintTextAlign: TextAlign.start, + padding: REdgeInsets.only(left: 10, top: 5, right: 5), + hintTextPadding: + REdgeInsets.only(left: 10, top: 5, right: 5), + backgroundColor: Colors.grey.withOpacity(0.1), + onFocusChanged: (hasFocus) { + debugPrint('has focus $hasFocus'); + if (hasFocus && FocusScope.of(context).hasFocus) { + controller.removeFocus(); + } + }, + // onTextChanged: (text) => + // debugPrint('widget text change $text'), + // onEditorCreated: () => debugPrint('Editor has been loaded'), + // onEditingComplete: (s) => debugPrint('Editing completed $s'), + onEditorResized: (height) => + debugPrint('Editor resized $height'), + onSelectionChanged: (sel) => + debugPrint('${sel.index},${sel.length}'), + loadingBuilder: (context) { + return Center( + child: CircularProgressIndicator( + strokeWidth: 0.4.r, + ), + ); + }, + ); + }), + ), + 24.verticalSpace, + Obx(() { + return IgnorePointer( + ignoring: controller.isButtonEnabled.isFalse, + child: CustomAppButton( + buttonText: widget.args.incidentsModel == null + ? "Submit" + : "Update", + buttonColor: controller.isButtonEnabled() + ? CustomAppColors.kSecondaryColor + : CustomAppColors.kLightGreyColor, + borderColor: controller.isButtonEnabled() + ? CustomAppColors.kSecondaryColor + : CustomAppColors.kLightGreyColor, + onTap: () { + controller.submitButtonPressed(context); + }, + ), + ); + }), + ], + ), + ), + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/all_care_notes_screen.dart b/lib/view/screens/clients/all_care_notes_screen.dart new file mode 100644 index 0000000..25cf8fe --- /dev/null +++ b/lib/view/screens/clients/all_care_notes_screen.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/all_care_notes_screen_contorller.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/allCareNotes/CarePlans.dart'; +import 'package:ftc_mobile_app/utilities/enums/care_note_form_type.dart'; +import 'package:get/get.dart'; +import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; + +class AllCareNotesScreenArgs { + final String serviceUserId; + + AllCareNotesScreenArgs({required this.serviceUserId}); +} + +class AllCareNotesScreen extends StatefulWidget { + final AllCareNotesScreenArgs args; + + const AllCareNotesScreen({super.key, required this.args}); + + @override + State createState() => _AllCareNotesScreenState(); +} + +class _AllCareNotesScreenState extends State { + final AllCareNotesScreenController controller = + Get.put(AllCareNotesScreenController()); + + @override + void initState() { + super.initState(); + + //Important + controller.serviceUserId = widget.args.serviceUserId; + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + controller.getCareNotesList(); + }); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + onBackButton: () => controller.backButtonPressed(context), + backgroundColor: CustomAppColors.kSmokeColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'All Care Notes', + ), + body: SafeArea( + child: _listView(), + ), + ); + } + + Widget _listView() { + return Obx(() { + final list = controller.notesList(); + final canLoadMore = controller.canLoadMore.value; + return SmartRefresher( + controller: controller.listRC, + scrollController: controller.listSC, + header: FrequentFunctions.waterDropHeader, + enablePullUp: canLoadMore, + onRefresh: controller.onRefresh, + onLoading: controller.onLoading, + child: (list.isEmpty) + ? Container( + color: Colors.white, + child: const Center( + child: Text("No data found"), + ), + ) + : ListView.separated( + itemCount: list.length, + itemBuilder: (_, index) { + final item = list[index]; + return _listItem(item); + }, + separatorBuilder: (_, i) => 8.verticalSpace, + ), + ); + }); + } + + Widget _listItem(CarePlan item) { + final isMoodRatingForm = + item.noteType == CareNotesFormType.moodRatingForm.apiValue; + + return InkWell( + onTap: () { + _onListItemTap(item); + }, + child: Container( + decoration: const BoxDecoration(color: Colors.white), + padding: REdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + CustomTextWidget( + text: item.noteType ?? "", + textAlign: TextAlign.left, + fontSize: 16.sp, + fontWeight: FontWeight.w500, + ), + (item.eventDateTime == null) + ? FrequentFunctions.noWidget + : CustomTextWidget( + text: DateFormatter.ddMMyyyyhhmmFormat( + DateTime.fromMillisecondsSinceEpoch( + item.eventDateTime!) + .toLocal()), + fontColor: Colors.grey, + textAlign: TextAlign.left, + fontSize: 10.sp, + ), + (item.isHTML != true) + ? CustomTextWidget( + text: isMoodRatingForm + ? (item.title ?? "") + : (item.noteDetails ?? ""), + textAlign: TextAlign.left, + fontColor: Colors.black54, + fontSize: 12.sp, + maxLines: 3, + fontWeight: FontWeight.w400, + ).paddingOnly(top: 6.r) + : FrequentFunctions.noWidget, + (item.flag == true) + ? CustomTextWidget( + text: "Handover Review", + fontColor: Colors.green, + textAlign: TextAlign.left, + fontSize: 14.sp, + fontWeight: FontWeight.w500, + ).paddingOnly(top: 6.r) + : FrequentFunctions.noWidget, + ], + ), + ), + const Icon(Icons.keyboard_arrow_right_rounded) + ], + ), + ), + ); + } + + void _onListItemTap(CarePlan item) { + Navigator.pushNamed(context, CustomRouteNames.kCareNoteDetailScreenRoute, + arguments: item); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/appointments_screen.dart b/lib/view/screens/clients/appointments_screen.dart new file mode 100644 index 0000000..83e6819 --- /dev/null +++ b/lib/view/screens/clients/appointments_screen.dart @@ -0,0 +1,259 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/dialogs/app_dialogs.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:get/get.dart'; +import 'package:grouped_list/grouped_list.dart'; +import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; +import '../../../ftc_mobile_app.dart'; +import '../../../models/appointmentsListResponse/AppointmentsListResponse.dart'; +import '../../custom_widgets/my_circle_image.dart'; + +class AppointmentScreen extends StatefulWidget { + final UserData userData; + + const AppointmentScreen({Key? key, required this.userData}) : super(key: key); + + @override + State createState() => _AppointmentScreenState(); +} + +class _AppointmentScreenState extends State { + final controller = Get.put(AppointmentScreenController()); + + @override + void initState() { + controller.serviceUserId = widget.userData.id ?? ""; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Appointments', + ), + body: Column( + children: [ + 16.verticalSpace, + MyCircleImage( + imageSize: 80.r, + url: "${WebUrls.baseUrl}${widget.userData.profilePictureUrl}", + errorWidget: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + height: 80.r, + width: 80.r, + ), + ), + 16.verticalSpace, + CustomTextWidget( + text: widget.userData.displayName, + fontSize: 14.sp, + fontWeight: FontWeight.w600), + 16.verticalSpace, + Expanded(child: _appointmentsList()), + ], + ), + ); + } + + Widget _appointmentsList() { + return ObxValue((RxList appointments) { + return SmartRefresher( + key: const ValueKey("refreshListAppointments"), + controller: controller.listRC, + scrollController: controller.listSC, + enablePullUp: false, + header: FrequentFunctions.waterDropHeader, + onRefresh: controller.onRefresh, + child: (appointments.isEmpty) + ? FrequentFunctions.centerText(text: "No appointments yet") + : GroupedListView( + elements: appointments(), + groupBy: (d) { + return DateTime.fromMillisecondsSinceEpoch(d.appointmentDate!) + .toLocal() + .isAfter(DateTime.now()) + ? 0 + : 1; + }, + groupSeparatorBuilder: (int groupByValue) { + final label = groupByValue == 0 + ? "Upcoming Appointments" + : "Previous Appointments"; + + return _buildGroupSeparatorWidget(label); + }, + itemBuilder: (context, element) => + _buildItemWidget(appointments.indexOf(element), element), + separator: 8.verticalSpace, + groupComparator: (item1, item2) { + return item1.compareTo(item2); + }, + ), + ); + }, controller.appointments); + } + + Widget _buildGroupSeparatorWidget(String susTag) { + return Container( + padding: EdgeInsets.only(left: 20.w), + child: CustomTextWidget( + alignment: Alignment.centerLeft, + isExpanded: false, + text: susTag, + fontSize: 16.sp, + fontWeight: FontWeight.w700), + ); + } + + Widget _buildItemWidget(int index, AppointmentsListResponseData data) { + return AppointmentWidget(data: data); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} + +class AppointmentWidget extends StatelessWidget { + final AppointmentsListResponseData data; + + const AppointmentWidget({ + super.key, + required this.data, + }); + + @override + Widget build(BuildContext context) { + return Container( + // height: 150, + alignment: Alignment.center, + margin: EdgeInsets.only(left: 20.w, bottom: 5.h, right: 10.w, top: 10.h), + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.h), + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: Colors.white, + border: Border( + left: BorderSide(color: Get.theme.primaryColor, width: 6), + top: BorderSide(color: Get.theme.primaryColor, width: 1), + right: BorderSide(color: Get.theme.primaryColor, width: 1), + bottom: BorderSide(color: Get.theme.primaryColor, width: 1), + ), + borderRadius: BorderRadius.all( + Radius.circular(2.0.r), + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CustomTextWidget( + text: data.staff?.name ?? "", + fontWeight: FontWeight.bold, + isExpanded: false), + CustomTextWidget( + text: data.appointmentStartTime ?? "", + fontSize: 10.sp, + fontWeight: FontWeight.w500, + fontColor: CustomAppColors.kLightTextColor, + isExpanded: false), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: CustomTextWidget( + text: data.appointmentDetails ?? "", + fontSize: 12.sp, + isExpanded: false, + maxLines: 2, + textAlign: TextAlign.left, + ), + ), + 10.horizontalSpace, + CustomTextWidget( + text: DateFormatter().getAppointmentTime( + DateTime.fromMillisecondsSinceEpoch(data.appointmentDate!)), + fontSize: 14.sp, + isExpanded: false, + fontWeight: FontWeight.w600, + ), + ], + ), + 10.verticalSpace, + SizedBox( + height: 32.r, + child: CustomAppButton( + onTap: () { + AppDialog.showAppointmentDetailDialog(data: data); + }, + buttonText: "View Appointment", + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + ), + ), + // SizedBox( + // height: 5.h, + // ), + // const ViewAppointmentButtonWidget( + // text: "+Add Note", + // textColor: CustomAppColors.kSecondaryColor, + // buttonColor: CustomAppColors.kPrimaryColor, + // borderColor: CustomAppColors.kSecondaryColor, + // ), + ], + ), + ); + } +} + +class ViewAppointmentButtonWidget extends StatelessWidget { + const ViewAppointmentButtonWidget({ + super.key, + required this.text, + required this.textColor, + required this.buttonColor, + this.borderColor, + }); + + final String text; + final Color textColor; + final Color buttonColor; + final Color? borderColor; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () {}, + child: Container( + width: MediaQuery.of(context).size.width, + alignment: Alignment.center, + padding: EdgeInsets.symmetric(vertical: 5.h), + margin: EdgeInsets.symmetric(horizontal: 2.w), + decoration: BoxDecoration( + border: borderColor != null ? Border.all(color: borderColor!) : null, + color: buttonColor, + borderRadius: BorderRadius.circular(2.r), + ), + child: CustomTextWidget( + text: text, + fontColor: textColor, + fontSize: 14.sp, + fontWeight: FontWeight.w700, + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/careNoteForms/ABC_form_screen.dart b/lib/view/screens/clients/careNoteForms/ABC_form_screen.dart new file mode 100644 index 0000000..ab9451f --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/ABC_form_screen.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import '../../../../controllers/clients/careNoteFormControllers/ABC_form_screen_controller.dart'; + +class ABCFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const ABCFormScreen({Key? key, required this.args}) : super(key: key); + + @override + State createState() => _ABCFormScreenState(); +} + +class _ABCFormScreenState extends State { + late final ABCFormScreenController controller; + + @override + void initState() { + controller = Get.put(ABCFormScreenController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add ABC Note', + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 16.verticalSpace, + InkWell( + onTap: () =>controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select...", + heading: "Date / Time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.antecedentEventsController, + textCapitalization: TextCapitalization.sentences, + heading: 'Antecedent Events', + hintText: 'Type here...', + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.behaviourController, + textCapitalization: TextCapitalization.sentences, + heading: 'Behaviour', + hintText: 'Type here...', + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.consequenceEventsController, + textCapitalization: TextCapitalization.sentences, + heading: 'Consequence Events', + hintText: 'Type here...', + onChange: (_) {}, + ), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/careNoteForms/consent_capacity_form_screen.dart b/lib/view/screens/clients/careNoteForms/consent_capacity_form_screen.dart new file mode 100644 index 0000000..679d346 --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/consent_capacity_form_screen.dart @@ -0,0 +1,663 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/consent_capacity_form_screen_controller.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class ConsentCapacityFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const ConsentCapacityFormScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => + _ConsentCapacityFormScreenState(); +} + +class _ConsentCapacityFormScreenState extends State { + late final ConsentCapacityFormScreenController controller; + + @override + void initState() { + controller = + Get.put(ConsentCapacityFormScreenController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add Consent, Capacity, MCA & DOLS note', + ), + body: SafeArea( + child: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 16.verticalSpace, + InkWell( + onTap: () => controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select...", + heading: "Date / Time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.commentsController, + heading: "Comments", + hintText: "Type comments here...", + onChange: (_) {}, + ), + 20.verticalSpace, + Padding( + padding: REdgeInsets.only(left: 8.0), + child: CustomTextWidget( + text: "Is an MCA required to be completed?", + isExpanded: false, + fontSize: 14.sp, + fontWeight: FontWeight.w400, + fontColor: Colors.black, + ), + ), + 8.verticalSpace, + _radioGroup(controller.selectedMCARequiredOption), + 20.verticalSpace, + Obx(() { + return controller.selectedMCARequiredOption() == "Yes" + ? Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: _otherFormWidgets(), + ) + : const SizedBox.shrink(); + }), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ), + ); + } + + Widget _radioGroup(Rx selectedOption) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: RadioButton( + value: 'Yes', + selectedOption: selectedOption, + ), + ), + 16.horizontalSpace, + Flexible( + child: RadioButton( + value: 'No', + selectedOption: selectedOption, + ), + ), + ], + ); + } + + List _otherFormWidgets() { + return [ + Html(data: """
+
+

This Mental Capacity Assessment must adhere to the Act’s 5 principles: +

+
    +
  • Every adult has the right to make his or her own decisions and must be assumed to + have capacity to make them unless proved otherwise. +
  • +
  • A person must be given all practicable help before anyone treat them as not being + able to make their own decisions. +
  • +
  • Just because an individual makes what may be seen as an unwise decision, they should + not be treated as lacking capacity to make that decision +
  • +
  • Anything done or any decision made on behalf of a person who lacks capacity must be + done in their best interests. +
  • +
  • Anything done or any decision made on behalf of a person who lacks capacity should + be the least restrictive of their basic rights and freedoms. +
  • +
+
+

This form has been developed to support compliance with the Mental + Capacity Act 2005. There is a statutory requirement for anyone undertaking an assessment to + have regard to the Code of Practice for the Mental Capacity Act. References given below + refer to the relevant paragraphs of the Mental Capacity Act Code of Practice. Please also + refer to MCA and DoLS Policy and Guidance. (For day to day decisions, please print out/ fill + in relevant sections 1.1 - 1.10)

+
+
"""), + 10.verticalSpace, + _multilineTextField( + controller: controller.mentalCapacityAssessmentDetailController, + heading: "Detail", + hintText: "Type here...", + ), + Html( + data: """
+

1.2 What is the specific decision relevant to this mental + capacity assessment? Please ensure that the decision is phrased in a way to enable + all viable options to be discussed. The MCA Code paragraph 4.4 states 'An assessment of a + person’s capacity must be based on their ability to make a specific decision at the time it + needs to be made, and not their ability to make decisions in general.'

+
+
""", + ), + 10.verticalSpace, + _multilineTextField( + controller: controller.specificDecisionDetailController, + heading: "Detail", + hintText: "Type here...", + ), + Html( + data: + """

1.3 Person undertaking/or who has undertaken this assessment of + capacity? The person with greatest responsibility for the specific decision is known as + the ‘decision-maker’ and should assess capacity. The decision maker is the person intending to + make the decision or carry out the action. Complex decisions may require specialist assessment - + seek guidance. See 4.38 to 4.43 of the Code.

+ """, + ), + 10.verticalSpace, + _singleLineTextField( + controller: controller.name1Controller, + heading: "Name", + hintText: "Type here...", + inputType: TextInputType.name, + ), + 20.verticalSpace, + _singleLineTextField( + controller: controller.roleController, + heading: "Role", + hintText: "Type here...", + ), + 20.verticalSpace, + _singleLineTextField( + controller: controller.organisationController, + heading: "Organisation", + hintText: "Type here...", + ), + 20.verticalSpace, + _singleLineTextField( + controller: controller.addressController, + heading: "Address", + hintText: "Type here...", + ), + 20.verticalSpace, + _singleLineTextField( + controller: controller.telController, + heading: "Tel", + hintText: "Type here...", + inputType: TextInputType.phone, + ), + 20.verticalSpace, + _singleLineTextField( + controller: controller.emailController, + heading: "Email", + hintText: "Type here...", + inputType: TextInputType.emailAddress, + ), + 20.verticalSpace, + InkWell( + onTap: () => controller.selectAssessmentDateTime(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.assessmentDateTimeController, + hintText: "Select...", + heading: "Date and time of assessment", + isEnabled: false, + onChange: (_) {}, + ), + ), + Html( + data: + """

1.4 What concerns/triggers have given rise to this assessment of + capacity? People have the right to make decisions that others might think are unwise. A + person who makes a decision that others think is unwise should not automatically be labelled as + lacking the capacity to make a decision. See MCA Code 4.35.

+

What is the reason to believe this person may lack capacity to make this particular decision? State your evidence:

+ """, + ), + 10.verticalSpace, + _multilineTextField( + controller: + controller.lackCapacityToMakeParticularDecisionDetailController, + heading: "Detail", + hintText: "Type here...", + ), + Html( + data: + """

1.5 Record your evidence here of the actions you have taken to + support the person. Consider what kind of help and support you can give the person to + help them understand, retain, weigh up information and communicate their decision.

+

Have you discussed with the person and/or appropriate others the most suitable venue + for the assessment? For example: Does the person feel more comfortable in their own + room? Does it need to be quiet? See MCA Code 3.13.

+

Have you discussed with the person and/or appropriate others to establish timing of + assessment For example: Is there a time of day that is better for the person? Would + it help to have a particular person present? See MCA Code 3.14.

+

Does the person have any language/communication issues? For example: Do + they have hearing or speech difficulties? Do you need an interpreter? Do they communicate + using special equipment e.g. a light talker communication device? See MCA Code 3.11.

+

Have you provided all the information, regarding all viable and available options + that the person needs to consider, to make an informed decision? See MCA Code 3.7. + The assessor must ensure that the person has:

+
    +
  1. Sufficiently detailed alternative plans explained to them to allow them to weigh up the + alternatives and make an informed choice where possible. +
  2. +
  3. Been supported by the assessor to explore the reasonably foreseeable consequences of + deciding one way or another, or failing to make the decision. +
  4. +
+
""", + ), + 10.verticalSpace, + _multilineTextField( + controller: controller.recordYourEvidenceDescribeController, + heading: "Describe", + hintText: "Type here...", + ), + 20.verticalSpace, + _multilineTextField( + controller: controller.viableOptionsConsideredController, + heading: "Viable options considered", + hintText: "Type here...", + ), + Html( + data: + """

If the decision is not urgent can it be delayed because the person + is likely to regain or develop the capacity to make it for themselves?

+
""", + ), + 10.verticalSpace, + ...List.generate(controller.canDecisionBeDelayedOptions.length, (index) { + final e = controller.canDecisionBeDelayedOptions[index]; + return ObxValue((RxBool isChecked) { + return CheckboxListTile( + value: isChecked(), + onChanged: isChecked, + controlAffinity: ListTileControlAffinity.trailing, + shape: const RoundedRectangleBorder( + side: BorderSide(color: CustomAppColors.kSmokeColor)), + tileColor: + (index % 2 == 0) ? CustomAppColors.kSmokeColor : Colors.white, + title: CustomTextWidget( + text: e.requirements, + isExpanded: false, + fontSize: 13.sp, + fontWeight: FontWeight.w400, + fontColor: Colors.black, + textAlign: TextAlign.left, + ), + ); + }, e.isChecked); + }), + 20.verticalSpace, + _multilineTextField( + controller: controller.explainWhyTickedBoxController, + heading: "Explain why you have ticked box(s)", + hintText: "Type here...", + ), + Html( + data: + """

1.6 Two Stage Capacity Assessment Answer the question + with facts. The questions cannot be answered with a simple “yes” or “no” and you are asked to + describe the assessment process. See MCA Code Ch. 4.

+

Stage 1. Is there an impairment or disturbance in the functioning of the person’s + mind or brain The person may not have a diagnosis but the Code says that proof of + an impairment or disturbance of the functioning of the mind or brain is required. You should + record here your reasons for believing this to be the case. See 4.11 - 4.12 of the Code. + This could be because of, for example, a head injury, a suspected infection or stroke, a + diagnosed dementia, mental illness, or learning disability.

+
""", + ), + _radioGroup(controller.selectedImpairmentOption), + 20.verticalSpace, + _multilineTextField( + controller: controller.impairmentDescribeController, + heading: "Describe", + hintText: "Type here...", + ), + Html( + data: """
+

If the person does not meet Stage 1, the assessment should immediately stop. Stage 2. + Record here how the identified impairment or disturbance in Stage 1 is affecting the + person’s ability to make the decision.See 4.13 to 4.30 of the Code.

+

Can the person understand the information relevant to the decision? See + 4.16 to 4.19 of the Code.

+
""", + ), + _radioGroup(controller.selectedCanPersonDecisionInfoOption), + 20.verticalSpace, + _multilineTextField( + controller: controller.describeCanPersonDecisionInfoController, + heading: "Describe how you assessed this", + hintText: "Type here...", + ), + Html( + data: + """

Can they retain that information long enough to make the decision? See 4.20 to 4.22 of the Code.

""", + ), + _radioGroup(controller.selectedCanTheyRetainOption), + 20.verticalSpace, + _multilineTextField( + controller: controller.describeCanTheyRetainController, + heading: "Describe how you assessed this", + hintText: "Type here...", + ), + Html( + data: + """

Can they use or weigh up that information as part of the process of making the decision? See 4.21 to 4.22 of the Code.

""", + ), + _radioGroup(controller.selectedCanTheyUseOption), + 20.verticalSpace, + _multilineTextField( + controller: controller.describeCanTheyUseController, + heading: "Describe how you assessed this", + hintText: "Type here...", + ), + Html( + data: + """

Can they communicate their decision, by any means available to them? See 4.23 to 4.25 of the Code.

""", + ), + _radioGroup(controller.selectedCanTheyCommunicateOption), + 20.verticalSpace, + _multilineTextField( + controller: controller.describeCanTheyCommunicateController, + heading: "Describe how you assessed this", + hintText: "Type here...", + ), + Html( + data: """
+

NB. If all of the answers to the four questions above are YES, then Stage 2 is not + met + and the assessment must end.

+

Stage 3: Causative Nexus There is a causative link between the impairment or + disturbance in the functioning of mind and brain AND the inability to make the required + decision. You must be able to evidence that the reason the person is unable to make the + decision is because of the impairment or disturbance in the functioning of mind or brain and + for no other reason.

+
""", + ), + ...List.generate(controller.causativeNexusOptions.length, (index) { + final e = controller.causativeNexusOptions[index]; + return ObxValue((RxBool isChecked) { + return CheckboxListTile( + value: isChecked(), + onChanged: isChecked, + controlAffinity: ListTileControlAffinity.trailing, + shape: const RoundedRectangleBorder( + side: BorderSide(color: CustomAppColors.kSmokeColor)), + tileColor: + (index % 2 == 0) ? CustomAppColors.kSmokeColor : Colors.white, + title: CustomTextWidget( + text: e.requirements, + isExpanded: false, + fontSize: 13.sp, + fontWeight: FontWeight.w400, + fontColor: Colors.black, + textAlign: TextAlign.left, + ), + ); + }, e.isChecked); + }), + 20.verticalSpace, + _multilineTextField( + controller: controller.evidenceController, + heading: "Evidence", + hintText: "Type here...", + ), + Html( + data: + """

1.7 Lack of mental capacity as a result of an impairment/disturbance in mind/brain + must + be distinguished from a situation where a person is unable to make their own decision as a + result of duress or undue influence. A person who has the mental capacity to make decisions may + have their ability to give free and true consent impaired if they are under constraint, coercion + or undue influence. Duress and undue influence may be affected by eroded confidence due to fear + of reprisal or abandonment, sense of obligation, cultural factors, power relationships or + coercive control within domestic abuse. Do you have a concern that the person may be under + duress/coercion or undue influence in relation to the making of this decision? If so, this will + not satisfy the Stage 1 (Diagnostic) test. You have to have an impairment or disturbance of the + mind or brain to satisfy that test.

Do you have a concern that the person may be under duress, coercion or undue influence?

""", + ), + _radioGroup(controller.selectedDoYouHaveConcernOption), + 20.verticalSpace, + _multilineTextField( + controller: controller.whatIsYourEvidenceController, + heading: "If yes, what is your evidence for saying this?", + hintText: "Type here...", + ), + 10.verticalSpace, + Html( + data: + """

If yes, what actions you intend to take (including consideration of seeking management/legal advice)

"""), + _multilineTextField( + controller: controller.seekingManagementDescribeController, + heading: "Describe", + hintText: "Type here...", + ), + Html( + data: + """

1.8 Please record here any further information or content of your interview with the person.

"""), + _multilineTextField( + controller: controller.recordInterviewDescribeController, + heading: "Describe", + hintText: "Type here...", + ), + Html( + data: + """

1.9 Determination of Capacity

+

I have assessed this person’s capacity to make the specific decision and determined on the + balance of probability that they do not have the capacity to make this decision at this + time.

"""), + _singleLineTextField( + controller: controller.dontHaveDecisionNameController, + heading: "Name", + hintText: "Type here...", + inputType: TextInputType.name, + ), + 20.verticalSpace, + InkWell( + onTap: () => controller.selectDontHaveDecisionDateTime(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dontHaveDecisionDateController, + hintText: "Select...", + heading: "Date", + isEnabled: false, + onChange: (_) {}, + ), + ), + Html( + data: + """

I have assessed this person’s capacity to make the specific decision and + determined that on the balance of probability that they have the capacity to make this decision + at this time.

"""), + _singleLineTextField( + controller: controller.haveDecisionNameController, + heading: "Name", + hintText: "Type here...", + inputType: TextInputType.name, + ), + 20.verticalSpace, + InkWell( + onTap: () => controller.selectHaveDecisionDateTime(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.haveDecisionDateController, + hintText: "Select...", + heading: "Date", + isEnabled: false, + onChange: (_) {}, + ), + ), + Html( + data: + """

Is an IMCA Required?

+
    +
  • If the person (16+) is unbefriended and the decision is about a change of accommodation, + or serious medical treatment, you MUST involve an IMCA. +
  • +
  • If a friend or family member exists, but they may not act in the person’s best interests + (for example because they are the alleged victim or abuser in a Safeguarding Adults + investigation) you MAY involve an IMCA. +
  • +
  • If the person is unbefriended and a health or social care review is being carried out, + you MAY CONSIDER involving an IMCA as good practice. +
  • +
  • Although you may involve an IMCA under the Mental Capacity Act legislation, if there is + no appropriate person, for people over age 18, you MUST instruct a Care Act Advocate if + the person has substantial difficulty engaging with the relevant assessment & + support planning/review/safeguarding process. Please use the most appropriate + legislation to ensure entitlement to advocacy. +
  • +
+

Does the individual require an IMCA?

+
"""), + _radioGroup(controller.selectedDoesRequireIMCAOption), + 20.verticalSpace, + _multilineTextField( + controller: controller.requireIMCAController, + heading: "If not, please give reasons.", + hintText: "Type here...", + ), + 20.verticalSpace, + InkWell( + onTap: () => controller.selectDateTimeOfWhyIMCARequired(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.whyIMCARequiredDateController, + hintText: "Select...", + heading: "Date and time of assessment", + isEnabled: false, + onChange: (_) {}, + ), + ), + Html( + data: """

Assessors Details.

""", + ), + 10.verticalSpace, + _singleLineTextField( + controller: controller.assessorsName4Controller, + heading: "Name", + hintText: "Type here...", + inputType: TextInputType.name, + ), + 20.verticalSpace, + _singleLineTextField( + controller: controller.designationController, + heading: "Designation", + hintText: "Type here...", + ), + 20.verticalSpace, + _singleLineTextField( + controller: controller.baseAddressController, + heading: "Base / Address", + hintText: "Type here...", + ), + 20.verticalSpace, + _singleLineTextField( + controller: controller.contactDetailsController, + heading: "Contact Details", + hintText: "Type here...", + ), + ]; + } + + Widget _multilineTextField( + {required TextEditingController controller, + required String heading, + required String hintText}) { + return CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 4, + maxLines: 4, + controller: controller, + heading: heading, + hintText: hintText, + onChange: (_) {}, + ); + } + + Widget _singleLineTextField( + {required TextEditingController controller, + required String heading, + required String hintText, + TextInputType inputType = TextInputType.text}) { + return CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller, + heading: heading, + hintText: hintText, + inputType: inputType, + onChange: (_) {}, + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/careNoteForms/export_care_note_forms.dart b/lib/view/screens/clients/careNoteForms/export_care_note_forms.dart new file mode 100644 index 0000000..183a917 --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/export_care_note_forms.dart @@ -0,0 +1,11 @@ +export 'package:ftc_mobile_app/view/screens/clients/careNoteForms/ABC_form_screen.dart'; +export 'package:ftc_mobile_app/view/screens/clients/careNoteForms/consent_capacity_form_screen.dart'; +export 'package:ftc_mobile_app/view/screens/clients/careNoteForms/free_text_entries_form_screen.dart'; +export 'package:ftc_mobile_app/view/screens/clients/careNoteForms/health_appointments_form_screen.dart'; +export 'package:ftc_mobile_app/view/screens/clients/careNoteForms/injury_health_issue_form_screen.dart'; +export 'package:ftc_mobile_app/view/screens/clients/careNoteForms/observations_form_screen.dart'; +export 'package:ftc_mobile_app/view/screens/clients/careNoteForms/physical_intervention_form_screen.dart'; +export 'package:ftc_mobile_app/view/screens/clients/careNoteForms/safeguarding_form_screen.dart'; +export 'package:ftc_mobile_app/view/screens/clients/careNoteForms/showering_bath_form_screen.dart'; +export 'package:ftc_mobile_app/view/screens/clients/careNoteForms/toileting_note_form_screen.dart'; +export 'package:ftc_mobile_app/view/screens/clients/careNoteForms/weight_height_form_screen.dart'; \ No newline at end of file diff --git a/lib/view/screens/clients/careNoteForms/free_text_entries_form_screen.dart b/lib/view/screens/clients/careNoteForms/free_text_entries_form_screen.dart new file mode 100644 index 0000000..9fd3bff --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/free_text_entries_form_screen.dart @@ -0,0 +1,139 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/free_text_entries_form_screen_controller.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class FreeTextEntriesFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const FreeTextEntriesFormScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => + _FreeTextEntriesFormScreenState(); +} + +class _FreeTextEntriesFormScreenState extends State { + late final FreeTextEntriesFormScreenController controller; + + @override + void initState() { + controller = + Get.put(FreeTextEntriesFormScreenController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add note', + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 16.verticalSpace, + InkWell( + onTap: () =>controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select date and time", + heading: "Event Date / Time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.titleController, + textCapitalization: TextCapitalization.words, + hintText: ConstantText.kTypeTitle, + heading: ConstantText.kTitle, + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.noteDetailsController, + textCapitalization: TextCapitalization.sentences, + heading: ConstantText.kNoteDetails, + hintText: ConstantText.kNoteDetailsHint, + onChange: (_) {}, + ), + 10.verticalSpace, + SizedBox( + height: 30.h, + child: GestureDetector( + onTap: controller.flagForHandover.toggle, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Obx( + () => Ink( + width: 32.r, + height: 32.r, + child: Checkbox( + value: controller.flagForHandover.value, + activeColor: CustomAppColors.kSecondaryColor, + onChanged: (value) { + controller.flagForHandover.value = value ?? false; + }, + ), + ), + ), + CustomTextWidget( + text: ConstantText.kFlagForHandover, + isExpanded: false, + fontSize: 10.sp, + fontWeight: FontWeight.w400, + ), + ], + ), + ), + ), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ); + } + + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/careNoteForms/health_appointments_form_screen.dart b/lib/view/screens/clients/careNoteForms/health_appointments_form_screen.dart new file mode 100644 index 0000000..8a4c408 --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/health_appointments_form_screen.dart @@ -0,0 +1,175 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/health_appointments_form_screen_controller.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class HealthAppointmentsFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const HealthAppointmentsFormScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => + _HealthAppointmentsFormScreenState(); +} + +class _HealthAppointmentsFormScreenState + extends State { + late final HealthAppointmentsFormScreenController controller; + + @override + void initState() { + controller = + Get.put(HealthAppointmentsFormScreenController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add Health Appointments note', + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 16.verticalSpace, + InkWell( + onTap: () => controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select...", + heading: "Date / Time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + _appointmentWithDropdown, + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.reasonController, + heading: "Reason for appointment", + hintText: "Reason for appointment", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.commentsController, + heading: "Comments", + hintText: "Type comments here...", + onChange: (_) {}, + ), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ); + } + + Widget get _appointmentWithDropdown { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.r), + border: Border.all( + color: CustomAppColors.kLightGreyColor, + width: 1.sp, + ), + ), + padding: EdgeInsets.symmetric( + vertical: 5.h, + horizontal: 15.w, + ), + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTextWidget( + text: "Appointment With", + fontSize: 10.sp, + fontWeight: FontWeight.w500, + fontColor: CustomAppColors.kLightTextColor, + alignment: Alignment.centerLeft, + ), + DropdownButtonHideUnderline( + child: DropdownButtonFormField( + onTap: () { + FocusScopeNode().unfocus(); + }, + dropdownColor: Colors.white, + decoration: const InputDecoration( + border: InputBorder.none, + ), + hint: Text( + "Appointment With", + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 14.sp, + color: CustomAppColors.kLightTextColor, + ), + ), + items: controller.appointmentWith + .map( + (e) => DropdownMenuItem( + value: e, + child: Text(e), + ), + ) + .toList(), + isExpanded: true, + iconSize: 20.h, + icon: Padding( + padding: REdgeInsets.only(right: 4.0), + child: + const Icon(Icons.arrow_drop_down_sharp, color: Colors.grey), + ), + onChanged: (category) { + controller.selectedAppointmentWith = category; + }, + ), + ), + ], + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/careNoteForms/injury_health_issue_form_screen.dart b/lib/view/screens/clients/careNoteForms/injury_health_issue_form_screen.dart new file mode 100644 index 0000000..9dc4b9e --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/injury_health_issue_form_screen.dart @@ -0,0 +1,263 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/injury_health_issue_form_screen_controller.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/clients/category_subcategory_dropdowns_widget.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class InjuryHealthIssueFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const InjuryHealthIssueFormScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => + _InjuryHealthIssueFormScreenState(); +} + +class _InjuryHealthIssueFormScreenState + extends State { + late final InjuryHealthIssueFormScreenController controller; + + @override + void initState() { + controller = + Get.put(InjuryHealthIssueFormScreenController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add Injury Health Issue note', + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 16.verticalSpace, + InkWell( + onTap: () =>controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select date and time", + heading: "Date and time of accident", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 4, + maxLines: 4, + controller: controller.nameOfWitnesses, + heading: "Name of witnesses/adults present", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 4, + maxLines: 4, + controller: controller.placeOfAccident, + heading: "Place accident occured", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 4, + maxLines: 4, + controller: controller.accidentDescription, + heading: "Description how the accident occured", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 4, + maxLines: 4, + controller: controller.recordOfInjury, + heading: "Record of any injury and action taken", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 4, + maxLines: 4, + controller: controller.conditionOfPatient, + heading: "Condition of the patient following of the accident", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + CategorySubcategoryDropdownsWidget( + controller: controller.catSubCatController, + ), + 20.verticalSpace, + Padding( + padding: REdgeInsets.only(left: 8.0), + child: CustomTextWidget( + text: "Parent Contacted", + isExpanded: false, + fontSize: 14.sp, + fontWeight: FontWeight.w400, + fontColor: Colors.black, + ), + ), + 8.verticalSpace, + radioGroup(), + Obx(() => controller.isParentContacted() == "Yes" + ? Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: widgetsIfParentContacted(), + ) + : const SizedBox.shrink()), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ); + } + + List widgetsIfParentContacted() { + return [ + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.nameOfParentContacted, + heading: "Name of parent contacted", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + InkWell( + onTap: () => controller.selectParentContactTime(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.parentContactedTime, + hintText: "Contact time", + heading: "Contact time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + Padding( + padding: REdgeInsets.only(left: 8.0), + child: CustomTextWidget( + text: "How parent was contacted", + isExpanded: false, + fontSize: 14.sp, + fontWeight: FontWeight.w400, + fontColor: Colors.black, + ), + ), + 8.verticalSpace, + howParentContactedRadioGroup(), + ]; + } + + Widget radioGroup() { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: RadioButton( + value: 'Yes', + selectedOption: controller.isParentContacted, + ), + ), + 16.horizontalSpace, + Flexible( + child: RadioButton( + value: 'No', + selectedOption: controller.isParentContacted, + ), + ), + ], + ); + } + + Widget howParentContactedRadioGroup() { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: RadioButton( + value: 'Call', + selectedOption: controller.howParentContacted, + ), + ), + 16.horizontalSpace, + Flexible( + child: RadioButton( + value: 'Email', + selectedOption: controller.howParentContacted, + ), + ), + 16.horizontalSpace, + Flexible( + child: RadioButton( + value: 'Text', + selectedOption: controller.howParentContacted, + ), + ), + ], + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/careNoteForms/mood_rating_form.dart b/lib/view/screens/clients/careNoteForms/mood_rating_form.dart new file mode 100644 index 0000000..a549259 --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/mood_rating_form.dart @@ -0,0 +1,110 @@ +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../../../controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import '../../../../controllers/clients/careNoteFormControllers/mood_rating_form_controller.dart'; + +class MoodRatingFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const MoodRatingFormScreen({super.key, required this.args}); + + @override + State createState() => _MoodRatingFormScreenState(); +} + +class _MoodRatingFormScreenState extends State { + late final MoodRatingFormController controller; + + @override + void initState() { + controller = Get.put(MoodRatingFormController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add Mood Rating note', + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + children: [ + 16.verticalSpace, + InkWell( + onTap: () => controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select date and time", + heading: "Event Date / Time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + //...Other widgets + ListView.separated( + shrinkWrap: true, + itemCount: controller.ratings.length, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, index) { + return Obx(() { + return CheckboxListTile( + value: controller.selectedRating() == + controller.ratings[index], + contentPadding: const EdgeInsets.symmetric(horizontal: 0), + secondary: Image.asset( + controller.ratings[index].icon, + width: 40.r, + height: 40.r, + ), + title: CustomTextWidget( + text: controller.ratings[index].name, + textAlign: TextAlign.left, + fontSize: 16.sp, + fontWeight: FontWeight.w600, + ), + controlAffinity: ListTileControlAffinity.trailing, + onChanged: (bool? value) { + if (value == true) { + controller.selectedRating.value = + controller.ratings[index]; + } + }, + ); + }); + }, + separatorBuilder: (_, index) => 10.verticalSpace, + ), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/careNoteForms/nutrition_hydration_form_screen.dart b/lib/view/screens/clients/careNoteForms/nutrition_hydration_form_screen.dart new file mode 100644 index 0000000..0710dec --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/nutrition_hydration_form_screen.dart @@ -0,0 +1,153 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/nutrition_hydration_form_screen_controller.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class NutritionHydrationFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const NutritionHydrationFormScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => + _NutritionHydrationFormScreenState(); +} + +class _NutritionHydrationFormScreenState + extends State { + late final NutritionHydrationFormScreenController controller; + + @override + void initState() { + controller = + Get.put(NutritionHydrationFormScreenController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add Nutrition Hydration Note', + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 16.verticalSpace, + InkWell( + onTap: () => controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select date and time", + heading: "Date / Time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + _radioGroup(controller.typeOptions, controller.selectedType), + 20.verticalSpace, + Obx(() { + return CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.mealDrinkTypeController, + textCapitalization: TextCapitalization.words, + heading: + (controller.selectedType() == NutritionHydrationType.food) + ? "Meal Type: (Breakfast, Lunch, Dinner, Snack etc)" + : 'Drink Type', + hintText: "Type here...", + onChange: (_) {}, + ); + }), + 20.verticalSpace, + Obx(() { + return CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.amountController, + textCapitalization: TextCapitalization.words, + heading: + (controller.selectedType() == NutritionHydrationType.food) + ? "Amount Eaten" + : "Amount (ML)", + hintText: "Type here...", + onChange: (_) {}, + ); + }), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.commentsController, + textCapitalization: TextCapitalization.sentences, + heading: "Comments", + hintText: "Type comments here...", + onChange: (_) {}, + ), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ); + } + + Widget _radioGroup(List options, Rx selected) { + options.map((e) => Flexible( + child: RadioButton( + value: e, + selectedOption: selected, + ), + )); + return Wrap( + runAlignment: WrapAlignment.start, + direction: Axis.horizontal, + runSpacing: 8.r, + spacing: 16.r, + children: options + .map((e) => RadioButton( + value: e, + selectedOption: selected, + )) + .toList(), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/careNoteForms/observations_form_screen.dart b/lib/view/screens/clients/careNoteForms/observations_form_screen.dart new file mode 100644 index 0000000..15ada9a --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/observations_form_screen.dart @@ -0,0 +1,135 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/observations_form_screen_controller.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class ObservationsFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const ObservationsFormScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => _ObservationsFormScreenState(); +} + +class _ObservationsFormScreenState extends State { + late final ObservationsFormScreenController controller; + + @override + void initState() { + controller = Get.put(ObservationsFormScreenController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add Health Observations note', + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 16.verticalSpace, + InkWell( + onTap: () =>controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select...", + heading: "Date / Time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + _singleLineTextField( + textEditingController: controller.heartRateController, + hint: "Heart Rate (BPM)", + heading: "Heart Rate (BPM)", + ), + 20.verticalSpace, + _singleLineTextField( + textEditingController: controller.bloodPressureController, + hint: "Blood Pressure (/MMHG)", + heading: "Blood Pressure (/MMHG)", + ), + 20.verticalSpace, + _singleLineTextField( + textEditingController: controller.respiratoryRateController, + hint: "Respiratory Rate", + heading: "Respiratory Rate", + ), + 20.verticalSpace, + _singleLineTextField( + textEditingController: controller.oxygenController, + hint: "Oxygen (%)", + heading: "Oxygen (%)", + ), + 20.verticalSpace, + _singleLineTextField( + textEditingController: controller.temperatureController, + hint: "Temperature (°C)", + heading: "Temperature (°C)", + ), + 20.verticalSpace, + _singleLineTextField( + textEditingController: controller.bloodSugarController, + hint: "Blood Sugar (MMOL/L)", + heading: "Blood Sugar (MMOL/L)", + ), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ); + } + + Widget _singleLineTextField( + {required TextEditingController textEditingController, + required String heading, + required String hint}) { + return CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + maxLength: 4, + controller: textEditingController, + heading: heading, + hintText: hint, + inputType: const TextInputType.numberWithOptions(), + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + onChange: (_) {}, + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/careNoteForms/physical_intervention_form_screen.dart b/lib/view/screens/clients/careNoteForms/physical_intervention_form_screen.dart new file mode 100644 index 0000000..6a6f44a --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/physical_intervention_form_screen.dart @@ -0,0 +1,329 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/physical_intervention_form_screen_controller.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/clients/category_subcategory_dropdowns_widget.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class PhysicalInterventionFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const PhysicalInterventionFormScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => + _PhysicalInterventionFormScreenState(); +} + +class _PhysicalInterventionFormScreenState + extends State { + late final PhysicalInterventionFormScreenController controller; + + @override + void initState() { + controller = + Get.put(PhysicalInterventionFormScreenController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add Physical Intervention note', + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 16.verticalSpace, + InkWell( + onTap: () => controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select...", + heading: "Date / Time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.durationOfIncidentController, + heading: "Duration of incident (Mins)", + hintText: "Type here...", + inputType: const TextInputType.numberWithOptions(), + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.staffDebriefFormNumberController, + heading: "Staff Debrief Form Number", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + _multilineTextField( + textEditingController: controller.nameOfWitnessController, + heading: "Name of witnesses/adults present", + hint: "Type here...", + ), + 20.verticalSpace, + _multilineTextField( + textEditingController: controller.incidentPlaceController, + heading: "Place incident occured", + hint: "Type here...", + ), + 20.verticalSpace, + _multilineTextField( + textEditingController: controller.whatWasUsedController, + heading: + "What was used prior to intervention to defuse/deescalae the situation?", + hint: "Type here...", + ), + 20.verticalSpace, + _multilineTextField( + textEditingController: controller.wasThePbsFollowedController, + heading: + "Was the PBS followed and was it sufficient enough to manage this incident?", + hint: "Type here...", + ), + 20.verticalSpace, + _multilineTextField( + textEditingController: controller.reasonForPhysicalInterventionController, + heading: "Reason for physical intervention", + hint: "Type here...", + ), + 20.verticalSpace, + _multilineTextField( + textEditingController: controller.staffInvolvedController, + heading: "Staff involved in the physical intervention", + hint: "Type here...", + ), + 20.verticalSpace, + _multilineTextField( + textEditingController: controller.conditionOfServiceUserController, + heading: + "Condition of service user following the incident, including breathing monitoring", + hint: "Type here...", + ), + 20.verticalSpace, + _multilineTextField( + textEditingController: controller.userClamedController, + heading: "How was the service user calmed?", + hint: "Type here...", + ), + 32.verticalSpace, + CustomTextWidget( + text: "Why was the use of force necessary?", + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w500, + fontColor: Colors.black, + ), + 12.verticalSpace, + ...List.generate(controller.whyForceNecessaryOptions.length, + (index) { + final e = controller.whyForceNecessaryOptions[index]; + return ObxValue((RxBool isChecked) { + return CheckboxListTile( + value: isChecked(), + onChanged: isChecked, + controlAffinity: ListTileControlAffinity.trailing, + shape: const RoundedRectangleBorder( + side: BorderSide(color: CustomAppColors.kSmokeColor)), + tileColor: (index % 2 == 0) + ? CustomAppColors.kSmokeColor + : Colors.white, + title: CustomTextWidget( + text: e.requirements, + isExpanded: false, + fontSize: 13.sp, + fontWeight: FontWeight.w400, + fontColor: Colors.black, + textAlign: TextAlign.left, + ), + ); + }, e.isChecked); + }), + 20.verticalSpace, + _multilineTextField( + textEditingController: controller.explainController, + heading: "If ticked \"Other\" please explain", + hint: "Type here...", + ), + 20.verticalSpace, + CategorySubcategoryDropdownsWidget( + controller: controller.catSubCatController, + ), + 20.verticalSpace, + Padding( + padding: REdgeInsets.only(left: 8.0), + child: CustomTextWidget( + text: "Parent Contacted", + isExpanded: false, + fontSize: 14.sp, + fontWeight: FontWeight.w400, + fontColor: Colors.black, + ), + ), + 8.verticalSpace, + _radioGroup(controller.isParentContactedOptions, + controller.isParentContacted), + Obx(() => controller.isParentContacted() == "Yes" + ? Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: widgetsIfParentContacted(), + ) + : const SizedBox.shrink()), + 20.verticalSpace, + _multilineTextField( + textEditingController: controller.commentsController, + heading: "Parent/carer's comments", + hint: "Type here...", + ), + 20.verticalSpace, + Padding( + padding: REdgeInsets.only(left: 8.0), + child: CustomTextWidget( + text: "How was this form shared with parents/carers?", + isExpanded: false, + fontSize: 14.sp, + fontWeight: FontWeight.w400, + fontColor: Colors.black, + ), + ), + 8.verticalSpace, + _radioGroup( + controller.howFormSharedOptions, controller.howFormSharedRx), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ); + } + + Widget _multilineTextField( + {required TextEditingController textEditingController, + required String heading, + required String hint}) { + return CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 4, + maxLines: 4, + controller: textEditingController, + heading: heading, + hintText: hint, + onChange: (_) {}, + ); + } + + List widgetsIfParentContacted() { + return [ + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.nameOfParentContacted, + heading: "Name of parent contacted", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + InkWell( + onTap: () => controller.selectParentContactTime(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.parentContactedTime, + hintText: "Contact time", + heading: "Contact time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + Padding( + padding: REdgeInsets.only(left: 8.0), + child: CustomTextWidget( + text: "How parent was contacted", + isExpanded: false, + fontSize: 14.sp, + fontWeight: FontWeight.w400, + fontColor: Colors.black, + ), + ), + 8.verticalSpace, + _radioGroup( + controller.howParentContactedOptions, controller.howParentContacted), + ]; + } + + Widget _radioGroup(List options, Rx selected) { + options.map((e) => Flexible( + child: RadioButton( + value: e, + selectedOption: selected, + ), + )); + return Wrap( + runAlignment: WrapAlignment.start, + direction: Axis.horizontal, + runSpacing: 8.r, + spacing: 16.r, + children: options + .map((e) => RadioButton( + value: e, + selectedOption: selected, + )) + .toList(), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/careNoteForms/safeguarding_form_screen.dart b/lib/view/screens/clients/careNoteForms/safeguarding_form_screen.dart new file mode 100644 index 0000000..dd07cd6 --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/safeguarding_form_screen.dart @@ -0,0 +1,214 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/safeguarding_form_screen_controller.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/clients/category_subcategory_dropdowns_widget.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class SafeguardingFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const SafeguardingFormScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => _SafeguardingFormScreenState(); +} + +class _SafeguardingFormScreenState extends State { + late final SafeguardingFormScreenController controller; + + @override + void initState() { + controller = Get.put(SafeguardingFormScreenController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add Safeguarding note', + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 16.verticalSpace, + InkWell( + onTap: () => controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select date and time", + heading: "Date and time of disclosure/findings", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 4, + maxLines: 4, + controller: controller.concernAboutServiceUserController, + heading: "Concerns about the service user", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 4, + maxLines: 4, + controller: controller.voiceOfServiceUserController, + heading: "Voice of the service user", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 4, + maxLines: 4, + controller: controller.anyImmediateRisksController, + heading: "Are there any immediate risks", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 4, + maxLines: 4, + controller: controller.qActionTakenController, + heading: "What action do you feel should be taken?", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 4, + maxLines: 4, + controller: controller.commentsController, + heading: "Comments", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + CategorySubcategoryDropdownsWidget( + controller: controller.catSubCatController), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.nameController1, + heading: "Your Name", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.anyWitnessesController, + heading: "Any witnesses", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + InkWell( + onTap: () => controller.selectDateAndTimeOfReporting(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.reportingDateTimeController, + hintText: "Select date and time", + heading: "Date and time of reporting", + isEnabled: false, + onChange: (_) {}, + ), + ), + 32.verticalSpace, + CustomTextWidget( + text: "To be completed by DSL/DDSL", + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w500, + fontColor: Colors.black, + ), + 12.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.nameController2, + heading: "Your Name", + hintText: "Type here...", + onChange: (_) {}, + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.actionTakenController, + heading: "Action taken", + hintText: "Type here...", + onChange: (_) {}, + ), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/careNoteForms/showering_bath_form_screen.dart b/lib/view/screens/clients/careNoteForms/showering_bath_form_screen.dart new file mode 100644 index 0000000..58b6ed0 --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/showering_bath_form_screen.dart @@ -0,0 +1,142 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/showering_bath_form_screen_controller.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class ShoweringBathFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const ShoweringBathFormScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => + _ShoweringBathFormScreenState(); +} + +class _ShoweringBathFormScreenState extends State { + late final ShoweringBathFormScreenController controller; + + @override + void initState() { + controller = Get.put(ShoweringBathFormScreenController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add ShoweringBath note', + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 16.verticalSpace, + InkWell( + onTap: () =>controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select...", + heading: "Date / Time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + radioGroup(), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.commentsController, + heading: "Comments", + hintText: "Type comments here...", + onChange: (_) {}, + ), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ); + } + + Widget radioGroup() { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: RadioButton( + value: 'Bath', + selectedOption: controller.selectedOption, + ), + ), + Expanded( + child: RadioButton( + value: 'Shower', + selectedOption: controller.selectedOption, + ), + ), + Expanded( + child: RadioButton( + value: 'Wash', + selectedOption: controller.selectedOption, + ), + ), + ], + ); + } + + // _selectDate() async { + // Get.focusScope?.unfocus(); + // final date = await CommonCode.datePicker(context); + // + // if (date != null) { + // controller.date = date; + // controller.dateController.text = + // CommonCode.careNoteDateFormatter.format(date); + // } + // } + + // _selectTime() async { + // TimeOfDay? timeOfDay = await CommonCode.selectTime(context, + // selectedTime: TimeOfDay.now(), + // themeColor: Get.theme.colorScheme.primary); + // + // if (timeOfDay != null) { + // controller.timeController.text = timeOfDay.toString(); + // controller.time = timeOfDay; + // } + // } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/careNoteForms/toileting_note_form_screen.dart b/lib/view/screens/clients/careNoteForms/toileting_note_form_screen.dart new file mode 100644 index 0000000..3ac3227 --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/toileting_note_form_screen.dart @@ -0,0 +1,169 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import '../../../../controllers/clients/careNoteFormControllers/toileting_note_form_screen_controller.dart'; + +class ToiletingNoteFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const ToiletingNoteFormScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => + _ToiletingNoteFormScreenState(); +} + +class _ToiletingNoteFormScreenState extends State { + late final ToiletingNoteFormScreenController controller; + + @override + void initState() { + controller = Get.put(ToiletingNoteFormScreenController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add Toileting note', + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 16.verticalSpace, + InkWell( + onTap: () =>controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select...", + heading: "Date / Time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + SizedBox( + height: 30.h, + child: GestureDetector( + onTap: controller.assistanceRequired.toggle, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Obx( + () => Ink( + width: 32.r, + height: 32.r, + child: Checkbox( + value: controller.assistanceRequired.value, + activeColor: CustomAppColors.kSecondaryColor, + onChanged: (value) { + controller.assistanceRequired.value = + value ?? false; + }, + ), + ), + ), + CustomTextWidget( + text: "Was assistance required with toileting?", + isExpanded: false, + fontSize: 14.sp, + fontWeight: FontWeight.w400, + ), + ], + ), + ), + ), + 20.verticalSpace, + Padding( + padding: REdgeInsets.only(left: 8.0), + child: CustomTextWidget( + text: "If yes, what assistance?", + isExpanded: false, + fontSize: 14.sp, + fontWeight: FontWeight.w400, + fontColor: Colors.black, + ), + ), + 8.verticalSpace, + radioGroup(), + 20.verticalSpace, + Obx(() { + return CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + isEnabled: controller.assistanceRequired(), + controller: controller.commentsController, + heading: "Comments", + hintText: "Type comments here...", + onChange: (_) {}, + ); + }), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ); + } + + Widget radioGroup() { + return Obx(() { + return IgnorePointer( + ignoring: controller.assistanceRequired.isFalse, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: RadioButton( + value: 'Bowel', + selectedOption: controller.selectedOption, + isEnabled: controller.assistanceRequired(), + ), + ), + 16.horizontalSpace, + Flexible( + child: RadioButton( + value: 'Urine', + selectedOption: controller.selectedOption, + isEnabled: controller.assistanceRequired(), + ), + ), + ], + ), + ); + }); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/careNoteForms/weight_height_form_screen.dart b/lib/view/screens/clients/careNoteForms/weight_height_form_screen.dart new file mode 100644 index 0000000..8d0d8f7 --- /dev/null +++ b/lib/view/screens/clients/careNoteForms/weight_height_form_screen.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/weight_height_form_screen_controller.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +class WeightHeightFormScreen extends StatefulWidget { + final CommonCareNoteFormArgs args; + + const WeightHeightFormScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => _WeightHeightFormScreenState(); +} + +class _WeightHeightFormScreenState extends State { + late final WeightHeightFormScreenController controller; + + @override + void initState() { + controller = Get.put(WeightHeightFormScreenController(args: widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Add Weight/Height note', + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 16.verticalSpace, + InkWell( + onTap: () => controller.selectDate(context), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.dateController, + hintText: "Select...", + heading: "Date / Time", + isEnabled: false, + onChange: (_) {}, + ), + ), + 20.verticalSpace, + Row( + children: [ + Expanded( + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + maxLength: 3, + controller: controller.heightController, + hintText: "Height (CM)", + heading: "Height (CM)", + inputType: const TextInputType.numberWithOptions(), + onChange: (_) {}, + ), + ), + 16.horizontalSpace, + Expanded( + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + maxLength: 3, + controller: controller.weightController, + hintText: 'Weight (KG)', + heading: 'Weight (KG', + inputType: const TextInputType.numberWithOptions(), + onChange: (_) {}, + ), + ), + ], + ), + 20.verticalSpace, + CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 6, + maxLines: 6, + controller: controller.commentsController, + heading: "Comments", + hintText: "Type comments here...", + onChange: (_) {}, + ), + 32.verticalSpace, + CustomAppButton( + buttonText: ConstantText.kSave, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: controller.onSaveButtonTap, + ), + 20.verticalSpace, + ], + ), + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/care_note_detail_screen.dart b/lib/view/screens/clients/care_note_detail_screen.dart new file mode 100644 index 0000000..3c7a71b --- /dev/null +++ b/lib/view/screens/clients/care_note_detail_screen.dart @@ -0,0 +1,191 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/utilities/enums/body_parts.dart'; +import 'package:ftc_mobile_app/utilities/enums/care_note_form_type.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import '../../../controllers/clients/care_note_detail_screen_controller.dart'; +import '../../../models/clients/allCareNotes/CarePlans.dart'; +import '../../custom_widgets/human_body_mapper_widget.dart'; + +class CareNoteDetailScreen extends StatefulWidget { + final CarePlan data; + + const CareNoteDetailScreen({super.key, required this.data}); + + static const meta = + """ +"""; + + static const css = """ +"""; + + @override + State createState() => _CareNoteDetailScreenState(); +} + +class _CareNoteDetailScreenState extends State { + late final CareNoteDetailScreenController controller; + + final commonHtmlStyle = { + "strong": Style( + display: Display.block, + fontSize: FontSize(16), + fontWeight: FontWeight.w700, + ), + "p, small": Style( + display: Display.block, + fontSize: FontSize(14), + ), + }; + + @override + void initState() { + super.initState(); + controller = Get.put(CareNoteDetailScreenController(widget.data)); + } + + @override + Widget build(BuildContext context) { + if (kDebugMode) { + log("CarePlan data: ${jsonEncode(widget.data.toJson())}"); + } + + final isMoodRatingForm = + widget.data.noteType == CareNotesFormType.moodRatingForm.apiValue; + + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + automaticallyImplyLeading: false, + title: const CustomTextWidget( + text: 'View Care Note', + fontColor: Colors.white, + textAlign: TextAlign.left, + ), + backgroundColor: Theme.of(context).primaryColor, + centerTitle: false, + actions: const [ + CloseButton(color: Colors.white), + ], + ), + body: SafeArea( + child: (widget.data.isHTML == true) + ? Obx(() { + return (controller.isLoadingWebPage()) + ? Center( + child: SizedBox.square( + dimension: 48.r, + child: const CircularProgressIndicator()), + ) + : ListView( + children: [ + Obx(() { + return SizedBox( + height: controller.webViewHeight(), + child: WebViewWidget( + controller: controller.webViewController), + ); + }), + // Flexible(child: WebViewWidget(controller: controller)), + bodyPoints(), + ], + ); + }) + : ListView( + children: [ + Html( + data: controller.headerHtml, + style: { + "strong": Style( + display: Display.block, + fontSize: FontSize(16), + fontWeight: FontWeight.w700, + ), + }, + ), + Html( + data: (widget.data.isHTML == true) + ? widget.data.noteDetails + : """

+ Title + ${widget.data.title ?? ""} +

+ ${(!isMoodRatingForm && widget.data.noteDetails.isNotNullOrEmpty()) ? "

${widget.data.noteDetails!}

" : ''} + ${(!isMoodRatingForm && widget.data.flag == true) ? """

Handover Review

""" : ''} + """, + shrinkWrap: true, + style: commonHtmlStyle, + ), + (widget.data.healthIssueId?.category == null) + ? FrequentFunctions.noWidget + : Html( + data: """

+ Effected Body Part + ${widget.data.healthIssueId!.category!.name} +

""", + style: commonHtmlStyle, + ), + bodyPoints().paddingOnly(top: 20.r), + ], + ), + ), + ); + } + + Widget bodyPoints() { + if (widget.data.healthIssueId?.category == null) { + return FrequentFunctions.noWidget; + } + + final Map map = {}; + + BodyPart.values + .where((e) => e.apiValue == widget.data.healthIssueId!.category!.enumed) + .forEach((e) { + map[e] = Colors.red; + }); + + return HumanBodyWidget( + width: Get.width * 0.8, + visibleBodyPoints: map, + ); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/care_notes_screen.dart b/lib/view/screens/clients/care_notes_screen.dart new file mode 100644 index 0000000..0c136cd --- /dev/null +++ b/lib/view/screens/clients/care_notes_screen.dart @@ -0,0 +1,142 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/care_note_category.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/clients/CareNoteOptionCard.dart'; +import 'package:get/get.dart'; + +import 'all_care_notes_screen.dart'; + +class CareNotesScreenArgs { + final String serviceUserId; + + CareNotesScreenArgs({required this.serviceUserId}); +} + +class CareNotesScreen extends StatefulWidget { + final CareNotesScreenArgs args; + + const CareNotesScreen({super.key, required this.args}); + + @override + State createState() => _CareNotesScreenState(); +} + +class _CareNotesScreenState extends State { + final CareNotesScreenController controller = + Get.put(CareNotesScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + onBackButton: () => controller.backButtonPressed(context), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () => controller.backButtonPressed(context), + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'Care Notes', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: SafeArea( + child: Padding( + padding: REdgeInsets.symmetric(horizontal: 18), + child: ListView( + children: [ + Row( + children: [ + Expanded( + child: AspectRatio( + aspectRatio: 1, + child: InkWell( + onTap: () { + Navigator.pushNamed(context, + CustomRouteNames.kAllCareNotesScreenRoute, + arguments: AllCareNotesScreenArgs( + serviceUserId: widget.args.serviceUserId)); + }, + child: const CareNoteOptionCard( + icon: AssetsManager.kIcGeneral, + name: "All Care Notes", + ), + ), + ), + ), + const Expanded(child: SizedBox.shrink()), + ], + ), + 16.verticalSpace, + CustomTextWidget( + alignment: Alignment.centerLeft, + isExpanded: false, + text: "Common Options", + fontSize: 16.sp, + fontWeight: FontWeight.w700), + 10.verticalSpace, + _categoriesList(), + // Flexible(child: ), + ], + ), + ), + ), + ); + } + + Widget _categoriesList() { + return GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, mainAxisSpacing: 6.r, crossAxisSpacing: 6.r), + itemCount: controller.categories.length, + itemBuilder: (_, index) { + return InkWell( + onTap: () => _onCategoryCardTap(controller.categories[index]), + child: CareNoteOptionCard( + icon: controller.categories[index].iconPath ?? "", + name: controller.categories[index].category ?? "", + ), + ); + }, + ); + } + + // ---------Methods----------- + void _onCategoryCardTap(CareNoteCategory category) { + Navigator.pushNamed( + context, + CustomRouteNames.kCareNotesSubcategoriesScreenRoute, + arguments: CareNotesSubcategoriesScreenArgs( + serviceUserId: widget.args.serviceUserId, + category: category, + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/care_notes_subcategories_screen.dart b/lib/view/screens/clients/care_notes_subcategories_screen.dart new file mode 100644 index 0000000..c45aaa7 --- /dev/null +++ b/lib/view/screens/clients/care_notes_subcategories_screen.dart @@ -0,0 +1,235 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/careNoteFormControllers/common_care_note_forms_controller.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/utilities/enums/care_note_form_type.dart'; +import 'package:get/get.dart'; +import '../../../controllers/clients/care_notes_subcategories_screen_controller.dart'; +import '../../../models/clients/care_note_category.dart'; + +class CareNotesSubcategoriesScreenArgs { + final String serviceUserId; + final CareNoteCategory category; + + CareNotesSubcategoriesScreenArgs( + {required this.serviceUserId, required this.category}); +} + +class CareNotesSubcategoriesScreen extends StatefulWidget { + final CareNotesSubcategoriesScreenArgs args; + + const CareNotesSubcategoriesScreen({super.key, required this.args}); + + @override + State createState() => + _CareNotesSubcategoriesScreenState(); +} + +class _CareNotesSubcategoriesScreenState + extends State { + final CareNotesSubcategoriesScreenController controller = + Get.put(CareNotesSubcategoriesScreenController()); + + List get _subcategories => + widget.args.category.subcategories ?? []; + + @override + Widget build(BuildContext context) { + return CustomScaffold( + onBackButton: () => controller.backButtonPressed(context), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: widget.args.category.category ?? "", + ), + // appBar: CustomAppBar( + // leadingButton: Container(), + // showBoxShadow: false, + // titleWidget: Row( + // children: [ + // InkWell( + // onTap: () => controller.backButtonPressed(context), + // child: CustomImageWidget( + // imagePath: AssetsManager.kBackIcon, + // height: 11.53.h, + // width: 8.66.w, + // ), + // ), + // SizedBox( + // width: 15.w, + // ), + // CustomTextWidget( + // text: widget.args.category.category ?? "", + // isExpanded: false, + // fontSize: 16.sp, + // fontWeight: FontWeight.w700, + // fontColor: CustomAppColors.kDarkBlueTextColor, + // ), + // ], + // ), + // ), + body: Padding( + padding: REdgeInsets.symmetric(horizontal: 18), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 10.verticalSpace, + Expanded(child: _categoriesList()), + ], + ), + ), + ); + } + + Widget _categoriesList() { + return GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, mainAxisSpacing: 6.r, crossAxisSpacing: 6.r), + itemCount: _subcategories.length, + itemBuilder: (_, index) { + return InkWell( + onTap: () { + _onSubcategoryCardTap(_subcategories[index]); + }, + child: Card( + elevation: 2, + shadowColor: CustomAppColors.kLightGreyColor, + surfaceTintColor: Colors.white, + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4).r), + child: Padding( + padding: REdgeInsets.all(8), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CustomImageWidget( + imagePath: _subcategories[index].iconPath ?? "", + height: 24.r, + width: 24.r, + ), + 12.verticalSpace, + CustomTextWidget( + text: _subcategories[index].name ?? "", + alignment: Alignment.center, + isExpanded: false, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kDarkBlueTextColor, + fontSize: 14.sp) + ], + ), + ), + ), + ); + }, + ); + } + + void _onSubcategoryCardTap(Subcategories subcategory) { + final args = CommonCareNoteFormArgs( + serviceUserId: widget.args.serviceUserId, + noteType: subcategory.apiValue!); + switch (CareNotesFormType.fromText(subcategory.formType ?? "")) { + case CareNotesFormType.injuryHealthIssueForm: + Navigator.pushNamed( + context, + CustomRouteNames.kInjuryHealthIssueFormScreenRoute, + arguments: args, + ); + break; + case CareNotesFormType.weightHeightForm: + Navigator.pushNamed( + context, + CustomRouteNames.kWeightHeightFormScreenRoute, + arguments: args, + ); + break; + case CareNotesFormType.healthAppointmentForm: + Navigator.pushNamed( + context, + CustomRouteNames.kHealthAppointmentsFormScreenRoute, + arguments: args, + ); + break; + case CareNotesFormType.observationsForm: + Navigator.pushNamed( + context, + CustomRouteNames.kObservationsFormScreenRoute, + arguments: args, + ); + break; + case CareNotesFormType.safeguardingForm: + Navigator.pushNamed( + context, + CustomRouteNames.kSafeguardingFormScreenRoute, + arguments: args, + ); + break; + case CareNotesFormType.showeringBathForm: + Navigator.pushNamed( + context, + CustomRouteNames.kShoweringBathFormScreenRoute, + arguments: args, + ); + break; + case CareNotesFormType.toiletingNoteForm: + Navigator.pushNamed( + context, + CustomRouteNames.kToiletingNoteFormScreenRoute, + arguments: args, + ); + break; + case CareNotesFormType.moodRatingForm: + Navigator.pushNamed( + context, + CustomRouteNames.kMoodRatingFormScreenRoute, + arguments: args, + ); + break; + case CareNotesFormType.ABCForm: + Navigator.pushNamed( + context, + CustomRouteNames.kABCFormScreenRoute, + arguments: args, + ); + break; + case CareNotesFormType.physicalInterventionForm: + Navigator.pushNamed( + context, + CustomRouteNames.kPhysicalInterventionFormScreenRoute, + arguments: args, + ); + break; + case CareNotesFormType.consentCapacityForm: + Navigator.pushNamed( + context, + CustomRouteNames.kConsentCapacityFormScreenRoute, + arguments: args, + ); + break; + case CareNotesFormType.nutritionHydrationForm: + Navigator.pushNamed( + context, + CustomRouteNames.kNutritionHydrationFormScreenRoute, + arguments: args, + ); + break; + default: + Navigator.pushNamed( + context, + CustomRouteNames.kFreeTextEntriesScreenRoute, + arguments: args, + ); + break; + } + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/care_plan_menu_screen.dart b/lib/view/screens/clients/care_plan_menu_screen.dart new file mode 100644 index 0000000..908eb58 --- /dev/null +++ b/lib/view/screens/clients/care_plan_menu_screen.dart @@ -0,0 +1,227 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../../ftc_mobile_app.dart'; +import '../../../models/profileData/user_data.dart'; +import '../../custom_widgets/my_circle_image.dart'; + +class CarePlanMenuScreen extends StatefulWidget { + final UserData userData; + + const CarePlanMenuScreen({Key? key, required this.userData}) + : super(key: key); + + @override + State createState() => _CarePlanMenuScreenState(); +} + +class _CarePlanMenuScreenState extends State { + late final CarePlanMenuScreenController controller; + + @override + void initState() { + controller = Get.put(CarePlanMenuScreenController(data: widget.userData)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + onBackButton: () => controller.backButtonPressed(context), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: "Care Plan Menu", + ), + body: Obx(() { + if (controller.serviceUser() == null) { + return FrequentFunctions.centerText(text: "User detail not found"); + } + + final detail = controller.serviceUser()!; + + return ListView( + children: [ + 16.verticalSpace, + UnconstrainedBox( + child: MyCircleImage( + imageSize: 80.r, + url: "${WebUrls.baseUrl}${detail.profilePictureUrl ?? ""}", + errorWidget: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + height: 80.r, + width: 80.r, + ), + ), + ), + SizedBox(height: 16.h), + CustomTextWidget( + text: detail.displayName, + fontSize: 14.sp, + fontWeight: FontWeight.w600), + 16.verticalSpace, + Container( + padding: EdgeInsets.only(left: 20.w), + child: CustomTextWidget( + alignment: Alignment.centerLeft, + isExpanded: false, + text: "Care Plans", + fontSize: 16.sp, + fontWeight: FontWeight.w700), + ), + // 5.verticalSpace, + // const LineWidget( + // text: "Key Contacts", + // ), + 5.verticalSpace, + LineWidget( + text: "Care Notes", + onItemTap: () { + Navigator.pushNamed( + controller.screenKey.currentContext!, + CustomRouteNames.kCareNotesScreenRoute, + arguments: CareNotesScreenArgs( + serviceUserId: controller.serviceUser()!.id!), + ); + }, + ), + 5.verticalSpace, + LineWidget( + text: "Risk Assesments", + onItemTap: () { + Navigator.pushNamed( + context, + CustomRouteNames.kRiskAssessmentsTemplateScreenRoute, + arguments: controller.serviceUser(), + ); + }), + 5.verticalSpace, + LineWidget( + text: "Consent and capacity", + onItemTap: () { + Navigator.pushNamed( + context, + CustomRouteNames + .kConsentAndCapacityQuestionnaireScreenRoute, + arguments: controller.serviceUser()!, + ); + }), + 5.verticalSpace, + LineWidget( + text: "Current Health Issues", + onItemTap: () { + Navigator.pushNamed( + context, + CustomRouteNames.kCurrentHealthIssuesScreenRoute, + arguments: controller.serviceUser()!, + ); + }), + 5.verticalSpace, + // LineWidget( + // text: "Life History & Goals", + // onItemTap: () { + // Navigator.pushNamed( + // context, + // CustomRouteNames.kLifeHistoryAndGoalsScreenRoute, + // arguments: controller.serviceUser.value, + // ); + // }), + // 5.verticalSpace, + // LineWidget( + // text: "Support Plan", + // onItemTap: () { + // Navigator.pushNamed( + // context, + // CustomRouteNames.kSupportPlanScreenRoute, + // arguments: controller.serviceUser.value, + // ); + // }), + // 5.verticalSpace, + // const LineWidget( + // text: "Medication Profile", + // ), + // 5.verticalSpace, + LineWidget( + text: "PBS Plan", + onItemTap: () { + Navigator.pushNamed( + context, + CustomRouteNames.kPBSPlanScreenRoute, + arguments: controller.serviceUser.value, + ); + }), + 5.verticalSpace, + LineWidget( + text: "Recent Incidents", + onItemTap: () { + Navigator.pushNamed( + context, CustomRouteNames.kRecentIncidentsScreenRoute, + arguments: controller.serviceUser.value); + }), + 5.verticalSpace, + LineWidget( + text: "Documents", + onItemTap: () { + Navigator.pushNamed( + context, + CustomRouteNames.kDocumentsListScreenRoute, + arguments: controller.serviceUser.value, + ); + }), + ], + ); + }), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} + +class LineWidget extends StatelessWidget { + const LineWidget({ + super.key, + required this.text, + this.onItemTap, + }); + + final Function? onItemTap; + final String text; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + if (onItemTap != null) { + onItemTap!(); + } + }, + child: Container( + margin: EdgeInsets.symmetric(horizontal: 15.w, vertical: 2.h), + padding: EdgeInsets.symmetric(horizontal: 10.r, vertical: 12.r), + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kSecondaryColor)), + child: Row( + children: [ + CustomTextWidget( + text: text, + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 16.sp), + const Spacer(), + Icon( + Icons.keyboard_arrow_right_rounded, + color: CustomAppColors.kSecondaryColor, + size: 18.r, + ), + ], + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/client_profile_screen.dart b/lib/view/screens/clients/client_profile_screen.dart new file mode 100644 index 0000000..1909cb1 --- /dev/null +++ b/lib/view/screens/clients/client_profile_screen.dart @@ -0,0 +1,321 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/my_circle_image.dart'; +import 'package:get/get.dart'; +import '../../../models/profileData/user_data.dart'; +import 'all_care_notes_screen.dart'; + +class ClientProfileScreen extends StatefulWidget { + final UserData userData; + + const ClientProfileScreen({Key? key, required this.userData}) + : super(key: key); + + @override + State createState() => _ClientProfileScreenState(); +} + +class _ClientProfileScreenState extends State { + late final ClientProfileScreenController controller; + + @override + void initState() { + controller = Get.put(ClientProfileScreenController(data: widget.userData)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + onBackButton: () => controller.backButtonPressed(context), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () => controller.backButtonPressed(context), + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + const SizedBox( + width: 15, + ), + CustomTextWidget( + text: 'Client: ${controller.serviceUser()?.displayName ?? ""}', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor), + ], + ), + ), + body: Obx(() { + if (controller.serviceUser() == null) { + return FrequentFunctions.centerText(text: "User detail not found"); + } + + final detail = controller.serviceUser()!; + + return SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox(height: 16.h), + MyCircleImage( + imageSize: 80.r, + url: "${WebUrls.baseUrl}${detail.profilePictureUrl ?? ""}", + errorWidget: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + height: 53.h, + width: 53.w, + ), + ), + 16.verticalSpace, + CustomTextWidget( + text: detail.displayName, + fontSize: 14.sp, + fontWeight: FontWeight.w600), + 16.verticalSpace, + BuildDetailRow( + title: 'Contact Number', + value: detail.modelId?.phoneNo ?? "", + ), + BuildDetailRow( + title: 'Home Address', + value: detail.modelId?.homeAddress ?? ""), + BuildDetailRow( + title: 'Next of Kin', + value: detail.modelId?.suFamilyHead ?? ""), + const BuildDetailRow(title: 'Diagnosis History', value: ''), + BuildRoundOutlinedBox( + context: context, + child: BuildDiagnosisHistoryList( + history: + (detail.modelId?.diagnosises.isNotNullOrEmpty() == true) + ? detail.modelId!.diagnosises.first.diagnosisText + : "", + date: (detail.modelId?.aboutPatient.aboutDate + .isNotNullOrEmpty() == + true) + ? DateFormatter().getAppointmentTime( + detail.modelId!.aboutPatient.aboutDate) + : "", + ), + ), + const BuildDetailRow(title: 'About the Patient', value: ''), + BuildRoundOutlinedBox( + context: context, + child: BuildDiagnosisHistoryList( + history: detail.modelId?.aboutPatient.aboutText ?? "", + date: (detail.modelId?.aboutPatient.aboutDate + .isNotNullOrEmpty() == + true) + ? DateFormatter().getAppointmentTime( + detail.modelId!.aboutPatient.aboutDate) + : "", + ), + ), + 16.verticalSpace, + BuildIconButton( + iconPath: AssetsManager.kNotesIcon, + text: 'Notes', + route: CustomRouteNames.kAllCareNotesScreenRoute, + arguments: AllCareNotesScreenArgs( + serviceUserId: controller.serviceUser()!.id!), + ), + BuildIconButton( + iconPath: AssetsManager.kCarePlanIcon, + text: 'Care Plan', + route: CustomRouteNames.kCarePlanMenuScreenRoute, + arguments: controller.serviceUser()!, + ), + BuildIconButton( + iconPath: AssetsManager.kCalendarAppointmentIcon, + text: 'Appointments', + route: CustomRouteNames.kAppointmentsScreenRoute, + arguments: controller.serviceUser()!, + ), + BuildIconButton( + iconPath: AssetsManager.kPhotoGalleryIcon, + text: 'Photo Gallery', + route: CustomRouteNames.kPhotoGalleryScreenRoute, + arguments: controller.serviceUser()!, + ), + ], + ), + ); + }), + ); + } +} + +class BuildIconButton extends StatelessWidget { + const BuildIconButton({ + super.key, + required this.iconPath, + required this.text, + required this.route, + required this.arguments, + }); + + final String iconPath; + final String text; + final String route; + final dynamic arguments; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () { + if (route.isNotEmpty) { + Navigator.pushNamed(context, route, arguments: arguments); + } + }, + child: Container( + margin: EdgeInsets.symmetric(vertical: 5.h, horizontal: 25.w), + padding: EdgeInsets.symmetric(horizontal: 10.r, vertical: 8.r), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(2.r), + border: Border.all( + color: CustomAppColors.kLightGreyColor.withOpacity(0.5))), + child: Row( + children: [ + CustomImageWidget( + imagePath: iconPath, + height: 20.r, + width: 20.r, + ), + 12.horizontalSpace, + CustomTextWidget( + text: text, + isExpanded: false, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kDarkBlueTextColor, + fontSize: 14.sp), + const Spacer(), + Icon( + Icons.arrow_forward_ios_rounded, + size: 12.sp, + ) + ], + ), + ), + ); + } +} + +class BuildDiagnosisHistoryList extends StatelessWidget { + const BuildDiagnosisHistoryList({ + super.key, + required this.date, + required this.history, + }); + + final String date; + final String history; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTextWidget( + text: date, + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 10.sp, + fontColor: CustomAppColors.kLightGreyColor, + ), + CustomTextWidget( + alignment: Alignment.centerLeft, + textAlign: TextAlign.left, + text: history, + isExpanded: false, + fontSize: 10.sp, + fontColor: CustomAppColors.kBlackColor, + ), + ], + ); + } +} + +class BuildRoundOutlinedBox extends StatelessWidget { + const BuildRoundOutlinedBox({ + super.key, + required this.context, + required this.child, + }); + + final BuildContext context; + final Widget child; + + @override + Widget build(BuildContext context) { + return Container( + alignment: Alignment.centerLeft, + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.symmetric(horizontal: 22.r), + padding: EdgeInsets.all(10.sp), + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kLightGreyColor), + borderRadius: BorderRadius.circular(10.r), + ), + child: child, + ); + } +} + +class BuildDetailRow extends StatelessWidget { + const BuildDetailRow({ + super.key, + required this.title, + required this.value, + }); + + final String title; + final String value; + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 20.w), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 4, + child: CustomTextWidget( + isExpanded: false, + text: '$title: ', + fontWeight: FontWeight.w600, + textAlign: TextAlign.left, + fontColor: CustomAppColors.kDarkBlueTextColor, + fontSize: 14.sp, + ), + ), + Expanded( + flex: 6, + child: CustomTextWidget( + text: value, + isExpanded: false, + maxLines: 2, + textAlign: TextAlign.right, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kLightTextColor, + fontSize: 14.sp), + ) + ], + ), + ); + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/add_new_pbs_plan_screen.dart b/lib/view/screens/clients/clients_new_view_module/add_new_pbs_plan_screen.dart new file mode 100644 index 0000000..a277b23 --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/add_new_pbs_plan_screen.dart @@ -0,0 +1,298 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/new_client_module_controllers/add_new_pbs_plan_screen_controller.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/PBSPlanModel.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:get/get.dart'; +import 'package:quill_html_editor/quill_html_editor.dart'; + +class AddNewPBSPlanScreenArgs { + final UserData userData; + final PbsList? pbsData; + final bool viewOnly; + + AddNewPBSPlanScreenArgs({ + required this.userData, + this.pbsData, + this.viewOnly = false, + }); +} + +class AddNewPBSPlanScreen extends StatefulWidget { + final AddNewPBSPlanScreenArgs args; + + const AddNewPBSPlanScreen({Key? key, required this.args}) : super(key: key); + + @override + State createState() => _AddNewPBSPlanScreenState(); +} + +class _AddNewPBSPlanScreenState extends State { + late final AddNewPbsPlanScreenController controller; + + @override + void initState() { + controller = Get.put(AddNewPbsPlanScreenController(widget.args)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + onBackButton: () => controller.backButtonPressed(context, null), + screenKey: controller.screenKey, + backgroundColor: CustomAppColors.kPrimaryColor, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + controller.backButtonPressed(context, null); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: (widget.args.viewOnly) + ? "View PBS Plan" + : widget.args.pbsData == null + ? 'Add New PBS Plan' + : 'Update PBS Plan', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: SafeArea( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 16.w), + child: SingleChildScrollView( + child: Obx( + () => Column( + children: [ + //About Plan + SeparateQuillEditorForFields( + headingText: 'About Plan', + headingBackGroundColor: + CustomAppColors.kLightGreyColor.withAlpha(150), + headingTextColor: CustomAppColors.kBlackColor, + // noteHintText: "About Plan hint", + noteText: controller.addPbsPlanModel.value.aboutPlan, + quillEditorController: controller + .addPbsPlanModel.value.aboutPlanQuillController, + viewOnly: (widget.args.viewOnly), + ), + Divider( + color: CustomAppColors.kLightGreyColor, + ), + + //Management of behavioural presentation + 10.verticalSpace, + SeparateQuillEditorForFields( + headingText: 'Management of behavioural presentation', + headingBackGroundColor: CustomAppColors.kDarkestGreenColor, + // noteHintText: "Management of behavioural presentation hint", + noteText: + controller.addPbsPlanModel().managementOfBehaviorPlan, + quillEditorController: controller.addPbsPlanModel.value + .managementOfBehaviouralPresentationQuillController, + viewOnly: (widget.args.viewOnly), + ), + Divider( + color: CustomAppColors.kLightGreyColor, + ), + + //Secondary Prevention + 10.verticalSpace, + SeparateQuillEditorForFields( + headingText: 'Secondary Prevention', + headingBackGroundColor: CustomAppColors.kDarkYellowColor, + // noteHintText: "Secondary Prevention hint", + noteText: + controller.addPbsPlanModel.value.secondaryPrevention, + quillEditorController: controller.addPbsPlanModel.value + .secondaryPreventionQuillController, + viewOnly: (widget.args.viewOnly), + ), + Divider( + color: CustomAppColors.kLightGreyColor, + ), + + //Reactive Strategies + 10.verticalSpace, + SeparateQuillEditorForFields( + headingText: 'Reactive Strategies', + headingBackGroundColor: CustomAppColors.kDarkRedColor, + // noteHintText: "Reactive Strategies hint", + noteText: + controller.addPbsPlanModel.value.reactiveStrategies, + quillEditorController: controller.addPbsPlanModel.value + .reactiveStrategiesQuillController, + viewOnly: (widget.args.viewOnly), + ), + Divider( + color: CustomAppColors.kLightGreyColor, + ), + + //Post Incident Support- Recovery + 10.verticalSpace, + SeparateQuillEditorForFields( + headingText: 'Post Incident Support- Recovery', + headingBackGroundColor: const Color(0xff3c78d8), + // noteHintText: "Post Incident Support- Recovery hint", + noteText: + controller.addPbsPlanModel.value.postIncidentSupport, + quillEditorController: controller.addPbsPlanModel.value + .postIncidentSupportRecoveryQuillController, + viewOnly: (widget.args.viewOnly), + ), + + //Submit Button + (widget.args.viewOnly) + ? FrequentFunctions.noWidget + : Obx( + () => CustomAppButton( + buttonText: widget.args.pbsData == null + ? "Submit" + : "Update", + buttonColor: controller.enableSubmitButton.isTrue + ? CustomAppColors.kSecondaryColor + : CustomAppColors.kLightGreyColor, + borderColor: controller.enableSubmitButton.isTrue + ? CustomAppColors.kSecondaryColor + : CustomAppColors.kLightGreyColor, + onTap: () { + if (controller.enableSubmitButton.isTrue) { + controller.submitButtonPressed(); + } + }, + ), + ), + 10.verticalSpace, + ], + ), + ), + ), + ), + ), + ); + } +} + +class SeparateQuillEditorForFields extends StatefulWidget { + const SeparateQuillEditorForFields({ + super.key, + required this.headingText, + this.headingTextColor = CustomAppColors.kWhiteColor, + required this.headingBackGroundColor, + this.headingBorderColor = CustomAppColors.kLightTextColor, + required this.noteText, + required this.quillEditorController, + // required this.noteHintText, + required this.viewOnly, + }); + + final String headingText; + final Color headingTextColor; + final Color headingBackGroundColor; + final Color headingBorderColor; + final String noteText; + + // final String noteHintText; + final bool viewOnly; + final QuillEditorController quillEditorController; + + @override + State createState() => + _SeparateQuillEditorForFieldsState(); +} + +class _SeparateQuillEditorForFieldsState + extends State { + @override + Widget build(BuildContext context) { + return Column( + children: [ + Container( + decoration: BoxDecoration( + color: widget.headingBackGroundColor, + border: Border.all(color: widget.headingBorderColor)), + padding: REdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: CustomTextWidget( + text: widget.headingText, + fontColor: widget.headingTextColor, + textAlign: TextAlign.left, + fontSize: 16.sp, + fontWeight: FontWeight.bold, + ), + ), + (widget.viewOnly) + ? FrequentFunctions.noWidget + : ToolBar( + toolBarColor: Colors.cyan.shade50, + activeIconColor: Colors.green, + padding: const EdgeInsets.all(8), + iconSize: 20, + controller: widget.quillEditorController, + ), + Container( + padding: EdgeInsets.only(bottom: 10.h), + child: QuillHtmlEditor( + hintText: "Type here...", + controller: widget.quillEditorController, + isEnabled: !widget.viewOnly, + minHeight: 250, + text: widget.noteText, + hintTextAlign: TextAlign.start, + hintTextStyle: TextStyle(fontSize: 14.sp, color: Colors.black54), + padding: const EdgeInsets.only(left: 10, top: 5, right: 5), + hintTextPadding: const EdgeInsets.only(left: 10, top: 5, right: 5), + backgroundColor: widget.headingBackGroundColor.withAlpha(25), + onFocusChanged: (hasFocus) { + // if (hasFocus && FocusScope.of(context).hasFocus) { + // controller.removeFocus(); + // } + }, + onTextChanged: (text) { + // controller.enableDisableSubmitButton(); + }, + // onEditorCreated: () => debugPrint('Editor has been loaded'), + onEditingComplete: (s) { + // controller.enableDisableSubmitButton(); + }, + onEditorResized: (height) => debugPrint('Editor resized $height'), + onSelectionChanged: (sel) => + debugPrint('${sel.index},${sel.length}'), + loadingBuilder: (context) { + return const Center( + child: CircularProgressIndicator( + strokeWidth: 0.4, + ), + ); + }, + ), + ), + ], + ); + } + + @override + void dispose() { + // Get.delete(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/crisis_management_screen.dart b/lib/view/screens/clients/clients_new_view_module/crisis_management_screen.dart new file mode 100644 index 0000000..3e65b1a --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/crisis_management_screen.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../../../ftc_mobile_app.dart'; + +class CrisisManagementScreen extends StatefulWidget { + const CrisisManagementScreen({Key? key}) : super(key: key); + + @override + State createState() => _CrisisManagementScreenState(); +} + +class _CrisisManagementScreenState extends State { + final CrisisManagementScreenController controller = + Get.put(CrisisManagementScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'Crisis management', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SingleChildScrollView( + child: Column( + children: [ + CustomTextWidget( + text: "What crisis looks like to me", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took"), + SizedBox(height: 8.h), + CustomTextWidget( + text: "How can I effectively be supported?", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus."), + SizedBox(height: 8.h), + ], + ), + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/export_client_new_view.dart b/lib/view/screens/clients/clients_new_view_module/export_client_new_view.dart new file mode 100644 index 0000000..e91de21 --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/export_client_new_view.dart @@ -0,0 +1,12 @@ +export 'crisis_management_screen.dart'; +export 'future_plans_screen.dart'; +export 'health_full_body_map_screen.dart'; +export 'health_screen.dart'; +export 'introduction_screen.dart'; +export 'medication_screen.dart'; +export 'my_current_plan_screen.dart'; +export 'mental_health_screen.dart'; +export 'my_interests_screen.dart'; +export 'support_plan_screen.dart'; +export 'overview_screen.dart'; +export 'things_i_want_you_to_help_me_screen.dart'; \ No newline at end of file diff --git a/lib/view/screens/clients/clients_new_view_module/future_plans_screen.dart b/lib/view/screens/clients/clients_new_view_module/future_plans_screen.dart new file mode 100644 index 0000000..c012d3e --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/future_plans_screen.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../../ftc_mobile_app.dart'; + +class FuturePlansScreen extends StatefulWidget { + const FuturePlansScreen({Key? key}) : super(key: key); + + @override + State createState() => _FuturePlansScreenState(); +} + +class _FuturePlansScreenState extends State { + final FuturePlansScreenController controller = Get.put(FuturePlansScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'Future Plans', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SingleChildScrollView( + child: Column( + children: [ + CustomTextWidget( + text: "What are my future plans?", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took"), + SizedBox(height: 8.h), + CustomTextWidget( + text: "How am I going to achieve them?", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus."), + SizedBox(height: 8.h), + ], + ), + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/health_full_body_map_screen.dart b/lib/view/screens/clients/clients_new_view_module/health_full_body_map_screen.dart new file mode 100644 index 0000000..d8e41d9 --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/health_full_body_map_screen.dart @@ -0,0 +1,566 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../../ftc_mobile_app.dart'; + +class HealthFullBodyMapScreen extends StatefulWidget { + const HealthFullBodyMapScreen({super.key}); + + @override + State createState() => _HealthFullBodyMapState(); +} + +class _HealthFullBodyMapState extends State { + HealthFullBodyMapScreenController controller = + Get.put(HealthFullBodyMapScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'Health Full Body Map', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: SingleChildScrollView( + child: Column( + children: [ + SizedBox(height: 8.h), + SizedBox( + width: MediaQuery.of(context).size.width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + listHeading(), + SizedBox( + height: 15.h, + ), + InkWell( + onTap: () { + controller.firstPointVisible.value = false; + controller.secondPointVisible.value = false; + controller.thirdPointVisible.value = false; + controller.fourthPointVisible.value = false; + controller.fifthPointVisible.value = false; + controller.sixthPointVisible.value = false; + }, + child: SizedBox( + height: MediaQuery.of(context).size.height / 1.22, + width: MediaQuery.of(context).size.width, + child: Stack( + children: [ + const Positioned( + // left: 60, + bottom: 0, + child: CustomImageWidget( + imagePath: AssetsManager.kEclipseIcon), + ), + const Positioned( + left: 100, + top: 5, + child: CustomImageWidget( + imagePath: AssetsManager.kManBodyImage), + ), + pointWidgets( + leftPosition: 162, + topPosition: 10, + colorOfPoint: CustomAppColors.kRedColor, + onPointTap: () { + controller.firstPointVisible.toggle(); + controller.secondPointVisible.value = false; + controller.thirdPointVisible.value = false; + controller.fourthPointVisible.value = false; + controller.fifthPointVisible.value = false; + controller.sixthPointVisible.value = false; + }, + ), + pointWidgets( + leftPosition: 218, + topPosition: 90, + colorOfPoint: CustomAppColors.kSecondaryColor, + onPointTap: () { + controller.secondPointVisible.toggle(); + controller.firstPointVisible.value = false; + controller.thirdPointVisible.value = false; + controller.fourthPointVisible.value = false; + controller.fifthPointVisible.value = false; + controller.sixthPointVisible.value = false; + }, + ), + pointWidgets( + leftPosition: 118, + topPosition: 140, + colorOfPoint: + CustomAppColors.kRedColor.withAlpha(250), + onPointTap: () { + controller.firstPointVisible.value = false; + controller.secondPointVisible.value = false; + controller.fourthPointVisible.value = false; + controller.fifthPointVisible.value = false; + controller.sixthPointVisible.value = false; + controller.thirdPointVisible.toggle(); + }, + ), + pointWidgets( + leftPosition: 150, + topPosition: 155, + colorOfPoint: + CustomAppColors.kRedColor.withAlpha(250), + onPointTap: () { + controller.firstPointVisible.value = false; + controller.secondPointVisible.value = false; + controller.thirdPointVisible.value = false; + controller.fifthPointVisible.value = false; + controller.sixthPointVisible.value = false; + controller.fourthPointVisible.toggle(); + }, + ), + pointWidgets( + leftPosition: 145, + topPosition: 320, + colorOfPoint: + CustomAppColors.kRedColor.withAlpha(250), + onPointTap: () { + controller.firstPointVisible.value = false; + controller.secondPointVisible.value = false; + controller.thirdPointVisible.value = false; + controller.fourthPointVisible.value = false; + controller.sixthPointVisible.value = false; + controller.fifthPointVisible.toggle(); + }, + ), + pointWidgets( + leftPosition: 190, + bottomPosition: 105, + colorOfPoint: + CustomAppColors.kRedColor.withAlpha(250), + onPointTap: () { + controller.firstPointVisible.value = false; + controller.secondPointVisible.value = false; + controller.thirdPointVisible.value = false; + controller.fourthPointVisible.value = false; + controller.fifthPointVisible.value = false; + controller.sixthPointVisible.toggle(); + }, + ), + triangleView( + leftPosition: 158, + topPosition: 22, + visibility: controller.firstPointVisible, + ), + issueDetailsDialog( + topPosition: 28, + leftPosition: 15, + showTriangle: false, + visibility: controller.firstPointVisible, + healthNote: "Headache", + selectedDropDownValue: + controller.firstPointSelectedDropdownValue, + complaint: "Issue No. 01, Issue No. 02", + lastUpdate: "Jul/0/2023", + ), + triangleView( + leftPosition: 213, + topPosition: 102, + visibility: controller.secondPointVisible, + ), + issueDetailsDialog( + topPosition: 108, + leftPosition: 35, + showTriangle: false, + visibility: controller.secondPointVisible, + healthNote: "Shoulders Muscle Pain", + selectedDropDownValue: + controller.secondPointSelectedDropdownValue, + complaint: "Issue No. 01, Issue No. 02", + lastUpdate: "Jul/0/2023", + ), + triangleView( + leftPosition: 114, + topPosition: 152, + visibility: controller.thirdPointVisible, + ), + issueDetailsDialog( + topPosition: 158, + leftPosition: 10, + showTriangle: false, + visibility: controller.thirdPointVisible, + healthNote: "Elbow Pain", + selectedDropDownValue: + controller.thirdPointSelectedDropdownValue, + complaint: "Issue No. 01, Issue No. 02", + lastUpdate: "Jul/0/2023", + ), + triangleView( + leftPosition: 146, + topPosition: 168, + visibility: controller.fourthPointVisible, + ), + issueDetailsDialog( + topPosition: 175, + leftPosition: 10, + showTriangle: false, + visibility: controller.fourthPointVisible, + healthNote: "Stomach Pain", + selectedDropDownValue: + controller.fourthPointSelectedDropdownValue, + complaint: "Issue No. 01, Issue No. 02", + lastUpdate: "Jul/0/2023", + ), + triangleView( + leftPosition: 140, + topPosition: 333, + visibility: controller.fifthPointVisible, + ), + issueDetailsDialog( + topPosition: 340, + leftPosition: 10, + showTriangle: false, + visibility: controller.fifthPointVisible, + healthNote: "Knee Pain", + selectedDropDownValue: + controller.fifthPointSelectedDropdownValue, + complaint: "Issue No. 01, Issue No. 02", + lastUpdate: "Jul/0/2023", + ), + triangleView( + leftPosition: 185, + bottomPosition: 95, + visibility: controller.sixthPointVisible, + ), + issueDetailsDialog( + topPosition: 430, + leftPosition: 10, + showTriangle: false, + visibility: controller.sixthPointVisible, + healthNote: "Foot Pain", + selectedDropDownValue: + controller.sixthPointSelectedDropdownValue, + complaint: "Issue No. 01, Issue No. 02", + lastUpdate: "Jul/0/2023", + ), + ], + ), + ), + ), + SizedBox( + height: 15.h, + ), + ], + ), + ), + ], + ), + ), + ), + ); + } + + Positioned triangleView({ + double? topPosition, + double? bottomPosition, + double? rightPosition, + double? leftPosition, + required RxBool visibility, + bool colorShow = false, + }) { + return Positioned( + top: topPosition, + left: leftPosition, + right: rightPosition, + bottom: bottomPosition, + child: Obx( + () => Container( + color: colorShow ? CustomAppColors.kGreenColor : null, + child: Visibility( + visible: visibility.value, + child: const CustomImageWidget( + imagePath: AssetsManager.kTriangleIcon, + ), + ), + ), + ), + ); + } + + Positioned pointWidgets({ + double? topPosition, + double? bottomPosition, + double? rightPosition, + double? leftPosition, + required Color colorOfPoint, + required VoidCallback onPointTap, + }) { + return Positioned( + top: topPosition, + left: leftPosition, + right: rightPosition, + bottom: bottomPosition, + child: InkWell( + onTap: onPointTap, + child: CircleAvatar( + radius: 5, + backgroundColor: colorOfPoint, + child: const CustomTextWidget( + text: "", + isExpanded: false, + ), + ), + ), + ); + } + + Widget issueDetailsDialog({ + double? topPosition, + double? bottomPosition, + double? rightPosition, + double? leftPosition, + bool showTriangle = true, + required RxBool visibility, + required String healthNote, + required String complaint, + required String lastUpdate, + required RxString selectedDropDownValue, + }) { + return Obx( + () => Positioned( + top: topPosition, + left: leftPosition, + right: rightPosition, + bottom: bottomPosition, + child: Visibility( + visible: visibility.value, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Visibility( + visible: showTriangle, + child: const CustomImageWidget( + imagePath: AssetsManager.kTriangleIcon, + ), + ), + Container( + width: 300, + height: 90, + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kLightGreyColor), + color: CustomAppColors.kWhiteColor, + boxShadow: [ + BoxShadow( + color: CustomAppColors.kLightGreyColor.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: const Offset(0, 3), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: EdgeInsets.only(left: 10.sp), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTextWidget( + text: 'Health Note', + isExpanded: false, + fontSize: 12.sp, + ), + CustomTextWidget( + text: healthNote, + fontSize: 14.sp, + fontWeight: FontWeight.bold, + isExpanded: false), + ], + ), + ), + Align( + alignment: Alignment.topRight, + child: Container( + alignment: Alignment.topRight, + margin: EdgeInsets.only(right: 5.w, top: 5.h), + height: 30.h, + width: 60.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.r), + color: selectedDropDownValue.value + .contains("Expired") + ? CustomAppColors.kRedColor.withAlpha(80) + : CustomAppColors.kGreenColor.withAlpha(80), + ), + child: FittedBox( + fit: BoxFit.scaleDown, + child: DropdownButtonHideUnderline( + child: DropdownButton( + padding: const EdgeInsets.all(0), + borderRadius: BorderRadius.circular(5.r), + isExpanded: false, + items: controller.activeList + .map( + (e) => DropdownMenuItem( + value: e, + child: CustomTextWidget( + alignment: Alignment.center, + fontColor: selectedDropDownValue + .value + .contains("Expired") + ? CustomAppColors.kRedColor + : CustomAppColors.kGreenColor, + text: e.toString(), + isExpanded: false, + ), + ), + ) + .toList(), + value: selectedDropDownValue.value, + onChanged: (value) { + selectedDropDownValue.value = + value.toString(); + }, + ), + ), + ), + ), + ), + // ), + ], + ), + Divider(color: CustomAppColors.kLightGreyColor, height: 5), + Container( + padding: EdgeInsets.only(left: 17.sp, top: 3.sp), + child: Text.rich( + textAlign: TextAlign.left, + TextSpan( + children: [ + TextSpan( + text: "Complaint: ", + style: TextStyle( + fontSize: 12.sp, fontWeight: FontWeight.w600), + ), + TextSpan( + text: complaint, + style: TextStyle( + fontSize: 12.sp, fontWeight: FontWeight.w400), + ), + ], + ), + ), + ), + Container( + padding: EdgeInsets.only(left: 17.sp, top: 3.sp), + child: Text.rich( + textAlign: TextAlign.left, + TextSpan( + children: [ + TextSpan( + text: "Last Update: ", + style: TextStyle( + fontSize: 12.sp, fontWeight: FontWeight.w600), + ), + TextSpan( + text: lastUpdate, + style: TextStyle( + fontSize: 12.sp, fontWeight: FontWeight.w400), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + } + + Widget listHeading() { + return Container( + decoration: const BoxDecoration( + border: Border( + top: BorderSide( + color: CustomAppColors.kSmokeColor, + ), + bottom: BorderSide(color: CustomAppColors.kSmokeColor), + ), + ), + child: Container( + padding: const EdgeInsets.only(left: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + decoration: BoxDecoration( + color: CustomAppColors.kBlueColor.withAlpha(150), + border: Border.all(color: CustomAppColors.kBlueColor), + borderRadius: BorderRadius.circular(50.r), + ), + padding: + const EdgeInsets.only(right: 5, left: 5, top: 5, bottom: 5), + child: CustomTextWidget( + textAlign: TextAlign.left, + text: "All Issues", + isExpanded: false, + fontWeight: FontWeight.w400, + fontSize: 14.sp), + ), + Container( + padding: const EdgeInsets.only(right: 0, top: 10, bottom: 10), + child: const CustomTextWidget( + textAlign: TextAlign.left, + text: "Head Pain", + isExpanded: false, + fontWeight: FontWeight.w400), + ), + Container( + padding: const EdgeInsets.only(right: 0, top: 10, bottom: 10), + child: CustomTextWidget( + text: "Nose Bone fracture", + isExpanded: false, + fontWeight: FontWeight.w400, + fontSize: 14.sp), + ), + ], + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/health_screen.dart b/lib/view/screens/clients/clients_new_view_module/health_screen.dart new file mode 100644 index 0000000..4501f38 --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/health_screen.dart @@ -0,0 +1,107 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../../ftc_mobile_app.dart'; + +class HealthScreen extends StatefulWidget { + const HealthScreen({Key? key}) : super(key: key); + + @override + State createState() => _HealthScreenState(); +} + +class _HealthScreenState extends State { + final HealthScreenController controller = Get.put(HealthScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'Health', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SingleChildScrollView( + child: Column( + children: [ + CustomTextWidget( + text: "What are we worried about and why?", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took"), + SizedBox(height: 8.h), + CustomTextWidget( + text: "What can we do to support?", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus."), + SizedBox(height: 140.h), + + Padding( + padding: EdgeInsets.zero, + child: CustomAppButton( + buttonText: ConstantText.kViewFullBodyMap, + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: (){ + Navigator.pushNamed(context, CustomRouteNames.kHealthFullBodyMapScreenRoute); + }, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/introduction_screen.dart b/lib/view/screens/clients/clients_new_view_module/introduction_screen.dart new file mode 100644 index 0000000..4db904c --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/introduction_screen.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import '../../../../ftc_mobile_app.dart'; + +class ClientIntroductionScreen extends StatefulWidget { + const ClientIntroductionScreen({super.key}); + + @override + State createState() => _ClientIntroductionScreenState(); +} + +class _ClientIntroductionScreenState extends State { + ClientsIntroductionScreenController controller = ClientsIntroductionScreenController(); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox(width: 15.w,), + CustomTextWidget( + text: "Introduction", + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + + body: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Column( + children: [ + // ClipRRect( + // borderRadius: BorderRadius.circular(50.r), + // child: CustomImageWidget( + // imagePath: FrequentFunctions + // .userModel.value.profilePictureUrl.isNotEmpty + // ? "${WebUrls.baseUrl}${FrequentFunctions.userModel.value.profilePictureUrl}" + // : AssetsManager.kPersonMainIcon, + // imageColor: FrequentFunctions + // .userModel.value.profilePictureUrl.isNotEmpty + // ? null + // : CustomAppColors.kLightTextColor, + // height: 80.h, + // width: 80.w, + // ), + // ), + SizedBox(height: 16.h), + CustomTextWidget( + text: 'user', + fontSize: 14.sp, + fontWeight: FontWeight.w600), + SizedBox(height: 16.h), + const Row( + children: [ + Expanded(child: BuildDetailSingleItem(title: 'Email', value: "jaylon.n@ftcservices.com",)), + Expanded(child: BuildDetailSingleItem(title: 'Contact Number', value: "+44 (0) 00 0000 0000",)), + ], + ), + const Row( + children: [ + Expanded(child: BuildDetailSingleItem(title: 'NI Number', value: "QQ 123456 C",)), + Expanded(child: BuildDetailSingleItem(title: 'DOB', value:"15/11/1996")), + ], + ), + const Row( + children: [ + Expanded(child: BuildDetailSingleItem(title: 'Post Code', value: "GL55 8PN",)), + Expanded(child: BuildDetailSingleItem(title: 'Kin', value:"12PO025")), + ], + ), + const Row( + children: [ + BuildDetailSingleItem(title: 'Address', value: "Gloucester, 1-2 Street Name",), + ], + ), + SizedBox(height: 16.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus."), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took"), + SizedBox(height: 8.h), + + ], + ), + ), + ), + ), + ); + + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/medication_screen.dart b/lib/view/screens/clients/clients_new_view_module/medication_screen.dart new file mode 100644 index 0000000..e1fab25 --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/medication_screen.dart @@ -0,0 +1,152 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../../ftc_mobile_app.dart'; + +class MedicationScreen extends StatefulWidget { + const MedicationScreen({super.key}); + + @override + State createState() => _MedicationScreenState(); +} + +class _MedicationScreenState extends State { + final MedicationScreenController controller = + Get.put(MedicationScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + onBackButton:()=> controller.backButtonPressed(context), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap:()=> controller.backButtonPressed(context), + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'Medication', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SingleChildScrollView( + child: Column( + children: [ + totalMedicinesNameRowField( + normalText: "Total medicines:", boldText: "2"), + SizedBox(height: 4.h), + + SizedBox(height: 24.h), + medicationHeading(headingText: "Pregabalin 50mg"), + SizedBox(height: 24.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took"), + SizedBox(height: 8.h), + Divider(), + SizedBox(height: 8.h), + medicationHeading(headingText: "Lamotrigene"), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took"), + SizedBox(height: 8.h), + ], + ), + ), + ), + ); + } + + ListTile medicationHeading({required String headingText}) { + return ListTile( + contentPadding: EdgeInsets.only(left: 12.w, right: 0.w), + title: CustomTextWidget( + text: headingText, + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + // tileColor: Colors.black.withOpacity(0.2), + trailing: SizedBox( + width: 60.w, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + InkWell( + onTap: () {}, + child: Container( + padding: EdgeInsets.all(1.sp), + decoration: BoxDecoration( + color: CustomAppColors.kYellowColor.withOpacity(0.5), + borderRadius: BorderRadius.circular(50.r), + ), + child: const Icon(Icons.edit,color: CustomAppColors.kDarkYellowColor,)), + ), + SizedBox(width: 3.w,), + InkWell( + onTap: () {}, + child: Container( + padding: EdgeInsets.all(1.sp), + decoration: BoxDecoration( + color: CustomAppColors.kRedColor.withOpacity(0.5), + borderRadius: BorderRadius.circular(50.r), + ), + child: const Icon(Icons.delete,color: CustomAppColors.kRedColor,)), + ), + ], + ), + ), + ); + } + + Row totalMedicinesNameRowField({required String boldText, required String normalText}) { + return Row( + children: [ + CustomTextWidget( + text: normalText, + isExpanded: false, + fontWeight: FontWeight.w400, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + + CustomTextWidget( + text: boldText, + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + ], + ); + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/mental_health_screen.dart b/lib/view/screens/clients/clients_new_view_module/mental_health_screen.dart new file mode 100644 index 0000000..2a8667d --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/mental_health_screen.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../../ftc_mobile_app.dart'; + +class MentalHealthScreen extends StatefulWidget { + const MentalHealthScreen({Key? key}) : super(key: key); + + @override + State createState() => _MentalHealthScreenState(); +} + +class _MentalHealthScreenState extends State { + final MentalHealthScreenController controller = Get.put(MentalHealthScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'Mental Health', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SingleChildScrollView( + child: Column( + children: [ + CustomTextWidget( + text: "the current presentation of mental health", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took"), + SizedBox(height: 8.h), + CustomTextWidget( + text: "Need Support to my current mental health", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus."), + SizedBox(height: 8.h), + ], + ), + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/my_current_plan_screen.dart b/lib/view/screens/clients/clients_new_view_module/my_current_plan_screen.dart new file mode 100644 index 0000000..783dfc8 --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/my_current_plan_screen.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../../ftc_mobile_app.dart'; + +class MyCurrentPlanScreen extends StatefulWidget { + const MyCurrentPlanScreen({Key? key}) : super(key: key); + + @override + State createState() => _MyCurrentPlanScreenState(); +} + +class _MyCurrentPlanScreenState extends State { + final MyCurrentPlanScreenController controller = Get.put(MyCurrentPlanScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'My current plan', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SingleChildScrollView( + child: Column( + children: [ + CustomTextWidget( + text: "My current plan", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took"), + SizedBox(height: 8.h), + CustomTextWidget( + text: "Current support that I need", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus."), + SizedBox(height: 8.h), + ], + ), + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/my_interests_screen.dart b/lib/view/screens/clients/clients_new_view_module/my_interests_screen.dart new file mode 100644 index 0000000..44413b3 --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/my_interests_screen.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../../ftc_mobile_app.dart'; + +class MyInterestsScreen extends StatefulWidget { + const MyInterestsScreen({Key? key}) : super(key: key); + + @override + State createState() => _MyInterestsScreenState(); +} + +class _MyInterestsScreenState extends State { + final MyInterestsScreenController controller = Get.put(MyInterestsScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'My Interests', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SingleChildScrollView( + child: Column( + children: [ + CustomTextWidget( + text: "Things I like doing", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took"), + SizedBox(height: 8.h), + CustomTextWidget( + text: "How I can support these activities?", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus."), + SizedBox(height: 8.h), + ], + ), + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/overview_screen.dart b/lib/view/screens/clients/clients_new_view_module/overview_screen.dart new file mode 100644 index 0000000..7ea66e1 --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/overview_screen.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../../ftc_mobile_app.dart'; + +class OverviewScreen extends StatefulWidget { + const OverviewScreen({Key? key}) : super(key: key); + + @override + State createState() => _OverviewScreenState(); +} + +class _OverviewScreenState extends State { + final OverViewScreenController controller = + Get.put(OverViewScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'Overview (What I want you to know about me)', + isExpanded: false, + fontSize: 13.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SingleChildScrollView( + child: Column( + children: [ + CustomTextWidget( + text: "What I want you to know about me", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took"), + SizedBox(height: 8.h), + CustomTextWidget( + text: "Current support that I need", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus."), + SizedBox(height: 8.h), + ], + ), + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/support_plan_screen.dart b/lib/view/screens/clients/clients_new_view_module/support_plan_screen.dart new file mode 100644 index 0000000..d080f0f --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/support_plan_screen.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../../ftc_mobile_app.dart'; + +class SupportPlanScreen extends StatefulWidget { + const SupportPlanScreen({Key? key}) : super(key: key); + + @override + State createState() => _SupportPlanScreenState(); +} + +class _SupportPlanScreenState extends State { + SupportPlanScreenController controller = + Get.put(SupportPlanScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox(width: 15.w,), + CustomTextWidget( + text: 'Support Plan Menu', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Center( + child: SingleChildScrollView( + child: Column( + // mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox(height: 16.h), + LineWidget( + text: "Medications", + onItemTap: () { + Navigator.pushNamed(context, + CustomRouteNames.kMedicationScreenRoute); + }), + SizedBox(height: 5.h,), + LineWidget(text: "Mental Health",onItemTap: (){ + Navigator.pushNamed(context, CustomRouteNames.kMentalHealthScreenRoute); + }), + SizedBox(height: 5.h,), + LineWidget(text: "Future Plans",onItemTap: (){ + Navigator.pushNamed(context, CustomRouteNames.kFuturePlansScreenRoute); + }), + SizedBox(height: 5.h,), + LineWidget(text: "Introduction",onItemTap: (){ + Navigator.pushNamed(context, CustomRouteNames.kClientsIntroductionScreenRoute); + }), + SizedBox(height: 5.h,), + LineWidget(text: "Overview",onItemTap: (){ + Navigator.pushNamed(context, CustomRouteNames.kOverViewScreenRoute); + }), + SizedBox(height: 5.h,), + LineWidget(text: "My Current Plan",onItemTap:(){ + Navigator.pushNamed(context, CustomRouteNames.kMyCurrentPlanScreenRoute); + }), + SizedBox(height: 5.h,), + LineWidget(text: "Things I want you to help me with",onItemTap: (){ + Navigator.pushNamed(context, CustomRouteNames.kThingsIWantYouToHelpScreenRoute); + }), + SizedBox(height: 5.h,), + LineWidget(text: "My interests",onItemTap: (){ + Navigator.pushNamed(context, CustomRouteNames.kMyInterestsScreenRoute); + }), + SizedBox(height: 5.h,), + LineWidget(text: "Health",onItemTap: (){ + Navigator.pushNamed(context, CustomRouteNames.kHealthScreenRoute); + }), + SizedBox(height: 5.h,), + LineWidget(text: "Crisis Management",onItemTap: (){ + Navigator.pushNamed(context, CustomRouteNames.kCrisisManagementScreenRoute); + }), + SizedBox(height: 5.h,), + + ], + ), + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/clients_new_view_module/things_i_want_you_to_help_me_screen.dart b/lib/view/screens/clients/clients_new_view_module/things_i_want_you_to_help_me_screen.dart new file mode 100644 index 0000000..50b943c --- /dev/null +++ b/lib/view/screens/clients/clients_new_view_module/things_i_want_you_to_help_me_screen.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../../../ftc_mobile_app.dart'; + +class ThingsIWantYouToHelpScreen extends StatefulWidget { + const ThingsIWantYouToHelpScreen({Key? key}) : super(key: key); + + @override + State createState() => _ThingsIWantYouToHelpScreenState(); +} + +class _ThingsIWantYouToHelpScreenState extends State { + final ThingsIWantYouToHelpMeWithScreenController controller = + Get.put(ThingsIWantYouToHelpMeWithScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'Things I want you to help me with', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SingleChildScrollView( + child: Column( + children: [ + CustomTextWidget( + text: "Things I want you to help me with", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took"), + SizedBox(height: 8.h), + CustomTextWidget( + text: "How I can help?", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + SizedBox(height: 8.h), + CustomTextWidget( + fontSize: 14.sp, + textAlign: TextAlign.left, + isExpanded: false, + text: + "It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus."), + SizedBox(height: 8.h), + ], + ), + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/consent_and_capacity_add_new_form_screen.dart b/lib/view/screens/clients/consent_and_capacity_add_new_form_screen.dart new file mode 100644 index 0000000..5d1ac23 --- /dev/null +++ b/lib/view/screens/clients/consent_and_capacity_add_new_form_screen.dart @@ -0,0 +1,317 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import '../../../ftc_mobile_app.dart'; +import '../../../models/clients/consent_details_model.dart'; +import '../../../models/profileData/user_data.dart'; + +class ConsentAndCapacityAddNewFormScreenArgs { + final UserData userData; + final ConsentDetailsModel? consentData; + + ConsentAndCapacityAddNewFormScreenArgs({ + required this.userData, + this.consentData, + }); +} + +class ConsentAndCapacityAddNewFormScreen extends StatefulWidget { + final ConsentAndCapacityAddNewFormScreenArgs args; + + const ConsentAndCapacityAddNewFormScreen({Key? key, required this.args}) + : super(key: key); + + @override + State createState() => + _ConsentAndCapacityAddNewFormScreenState(); +} + +class _ConsentAndCapacityAddNewFormScreenState + extends State { + final controller = Get.put(ConsentAndCapacityAddNewFormScreenController()); + + @override + void initState() { + controller.isForUpdate.value = (widget.args.consentData != null); + + controller.firstNameController.text = + widget.args.userData.modelId?.suFirstMiddleName ?? ""; + controller.lastNameController.text = + widget.args.userData.modelId?.suLastName ?? ""; + controller.phoneController.text = + widget.args.userData.modelId?.phoneNo ?? ""; + controller.emailController.text = + widget.args.userData.modelId?.suEmailWork ?? ""; + controller.genderController.text = + widget.args.userData.modelId?.suSex ?? ""; + controller.dobController.text = (widget.args.userData.modelId?.suDob == + null) + ? "" + : DateFormat("MMM/dd/yyyy").format(widget.args.userData.modelId!.suDob); + controller.ageController.text = widget.args.userData.modelId?.suAge ?? ""; + controller.descriptionController.text = + widget.args.consentData?.description ?? ""; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + onBackButton: () => controller.backButtonPressed(context), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Column( + children: [ + Row( + children: [ + InkWell( + onTap: () => controller.backButtonPressed(context), + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + 15.horizontalSpace, + Obx( + () => CustomTextWidget( + text: controller.isForUpdate.value + ? 'Update Consent And Capacity' + : 'Consent And Capacity', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ), + ], + ), + CustomTextWidget( + alignment: Alignment.topLeft, + textAlign: TextAlign.left, + text: ' Add New Form', + isExpanded: false, + fontSize: 12.sp, + fontColor: CustomAppColors.kLightGreyColor, + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SingleChildScrollView( + child: Column( + children: [ + CustomTextWidget( + text: "Young Information", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + 20.verticalSpace, + _textField( + controller: controller.firstNameController, + enabled: false, + headingText: "First & Middle Name", + hintText: "First & Middle Name", + ), + 20.verticalSpace, + _textField( + controller: controller.lastNameController, + enabled: false, + headingText: "Last Name", + hintText: "Last Name", + ), + 20.verticalSpace, + _textField( + controller: controller.phoneController, + enabled: false, + headingText: "Phone No.", + hintText: "Phone No.", + ), + 20.verticalSpace, + _textField( + controller: controller.emailController, + enabled: false, + headingText: "Email", + hintText: "Email", + ), + 20.verticalSpace, + _textField( + controller: controller.genderController, + enabled: false, + headingText: "Sex", + hintText: "Sex", + ), + 20.verticalSpace, + Row( + children: [ + Expanded( + flex: 2, + child: _textField( + controller: controller.dobController, + enabled: false, + headingText: "Date Of Birth", + hintText: "Date Of Birth", + ), + ), + 10.horizontalSpace, + Expanded( + flex: 1, + child: _textField( + controller: controller.ageController, + enabled: false, + headingText: "Age", + hintText: "Age", + ), + ), + ], + ), + 20.verticalSpace, + CustomTextFieldWidget( + controller: controller.descriptionController, + heading: "Description", + minLines: 4, + maxLines: 4, + ), + 20.verticalSpace, + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.r), + ), + backgroundColor: CustomAppColors.kSecondaryColor), + onPressed: () async { + controller.isForUpdate.value + ? controller.updateButtonPressed( + widget.args.consentData!.id) + : await controller + .saveButtonPressed(widget.args.userData.id!); + + Navigator.of(context).pop(controller.isForUpdate.value + ? controller.descriptionController.text + : controller.saveResult); + }, + child: Obx( + () => CustomTextWidget( + text: controller.isForUpdate.value + ? "Update" + : 'Save', + fontColor: CustomAppColors.kPrimaryColor), + ), + ), + ), + 5.horizontalSpace, + Expanded( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + side: const BorderSide( + color: CustomAppColors.kSecondaryColor), + borderRadius: BorderRadius.circular(5.r), + ), + backgroundColor: CustomAppColors.kPrimaryColor, + ), + onPressed: () { + Navigator.of(context).pop(); + }, + child: CustomTextWidget( + text: 'Cancel', + fontColor: CustomAppColors.kSecondaryColor, + fontSize: 15.sp, + ), + ), + ), + ], + ), + 20.verticalSpace, + ], + ), + ), + ), + ); + } + + TextField _textField({ + required String headingText, + required String hintText, + int minLines = 1, + int maxLines = 1, + TextEditingController? controller, + bool enabled = true, + TextInputType? inputType, + }) { + return TextField( + enabled: enabled, + maxLines: maxLines, + minLines: minLines, + controller: controller, + textAlign: TextAlign.left, + keyboardType: inputType, + decoration: InputDecoration( + isDense: true, + border: const OutlineInputBorder(), + hintText: hintText, + labelText: headingText, + ), + ); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} + +class ConsentScreenTextField extends StatelessWidget { + const ConsentScreenTextField({ + super.key, + required this.headingText, + required this.hintText, + this.minLines, + this.maxLines, + this.fieldIcon, + this.fieldController, + this.viewOnly, + }); + + final String headingText; + + final String hintText; + + final int? minLines; + final int? maxLines; + final IconData? fieldIcon; + final bool? viewOnly; + final TextEditingController? fieldController; + + @override + Widget build(BuildContext context) { + return TextField( + enabled: !(viewOnly != null ? viewOnly == true : fieldController == null), + maxLines: maxLines, + minLines: minLines, + textAlign: TextAlign.left, + controller: fieldController ?? (TextEditingController()..text = hintText), + decoration: InputDecoration( + suffixIcon: Icon(fieldIcon), + border: const OutlineInputBorder(), + hintText: hintText, + labelText: headingText, + ), + ); + } +} diff --git a/lib/view/screens/clients/consent_and_capacity_questionnaire_screen.dart b/lib/view/screens/clients/consent_and_capacity_questionnaire_screen.dart new file mode 100644 index 0000000..0f34c9f --- /dev/null +++ b/lib/view/screens/clients/consent_and_capacity_questionnaire_screen.dart @@ -0,0 +1,225 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/custom_app_bar_with_action.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import '../../../ftc_mobile_app.dart'; +import '../../../models/clients/consent_details_model.dart'; +import '../../../models/profileData/user_data.dart'; + +class ConsentAndCapacityQuestionnaireScreen extends StatefulWidget { + final UserData userData; + + const ConsentAndCapacityQuestionnaireScreen( + {Key? key, required this.userData}) + : super(key: key); + + @override + State createState() => + _ConsentAndCapacityQuestionnaireScreenState(); +} + +class _ConsentAndCapacityQuestionnaireScreenState + extends State { + late final ConsentAndCapacityQuestionnaireScreenController controller; + + @override + void initState() { + controller = Get.put( + ConsentAndCapacityQuestionnaireScreenController(data: widget.userData)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + // onBackButton: () => controller.backButtonPressed(context), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + // sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: _appBar(context), + body: SafeArea( + child: Obx(() { + if (controller.serviceUser() == null) { + return FrequentFunctions.centerText(text: "User detail not found"); + } + + final detail = controller.serviceUser()!; + return Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SingleChildScrollView( + child: Column( + children: [ + CustomTextWidget( + text: "Questionnaire", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + 16.verticalSpace, + nameRowField(boldText: "Name:", normalText: detail.displayName), + 4.verticalSpace, + nameRowField( + boldText: "DOB:", + normalText: detail.modelId?.suDob == null + ? "" + : DateFormatter() + .getHolidayDate(detail.modelId!.suDob)), + 4.verticalSpace, + nameRowField( + boldText: "Sex:", + normalText: detail.modelId?.suSex ?? ""), + 4.verticalSpace, + nameRowField( + boldText: "Contact No:", + normalText: detail.modelId?.phoneNo ?? ""), + 4.verticalSpace, + nameRowField( + boldText: "Email:", + normalText: detail.modelId?.suEmailWork ?? ""), + 24.verticalSpace, + Obx( + () => ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + separatorBuilder: (context, index) => 8.verticalSpace, + itemCount: controller.consentDetailsList.length, + itemBuilder: (BuildContext context, int index) { + return _listItem( + index, controller.consentDetailsList[index]); + }, + ), + ), + 24.verticalSpace, + ], + ), + ), + ); + }), + ), + ); + } + + AppBar _appBar(BuildContext context) { + return CustomAppBarWithAction( + context, + titleText: "Consent And Capacity", + actionText: '+ Add New', + onActionTap: () async { + final result = await Navigator.pushNamed( + context, CustomRouteNames.kConsentAndCapacityAddNewFormScreenRoute, + arguments: ConsentAndCapacityAddNewFormScreenArgs( + userData: widget.userData, + )); + if (result is ConsentDetailsModel) { + controller.consentDetailsList.insert(0, result); + } + }, + ); + } + + Widget _listItem(int index, ConsentDetailsModel data) { + return Container( + key: ObjectKey(data), + decoration: BoxDecoration( + color: CustomAppColors.kLightGreyColor.withOpacity(0.2), + ), + padding: REdgeInsets.all(8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + CustomTextWidget( + text: data.description, + isExpanded: false, + textAlign: TextAlign.left, + fontSize: 16, + ).paddingSymmetric(horizontal: 8.r), + 6.verticalSpace, + Row( + children: [ + Expanded( + child: CustomTextWidget( + text: DateFormat("dd/MM/yyyy / hh:mm:ss aa") + .format(data.updatedAt) + .toUpperCase(), + isExpanded: false, + textAlign: TextAlign.left, + fontSize: 14, + ).paddingSymmetric(horizontal: 8.r), + ), + // InkWell( + // borderRadius: 24.toRadius(), + // onTap: () { + // _onEditClick(index, data); + // }, + // child: + // Icon(Icons.mode_edit_outlined, size: 22.r).addPaddingAll(4), + // ), + + ], + ) + ], + ), + ); + } + + Row nameRowField({required String boldText, required String normalText}) { + return Row( + children: [ + CustomTextWidget( + text: boldText, + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 16.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + 8.horizontalSpace, + Expanded( + child: CustomTextWidget( + text: normalText, + isExpanded: false, + fontWeight: FontWeight.w400, + fontSize: 14.sp, + textAlign: TextAlign.left, + alignment: Alignment.topLeft), + ), + ], + ); + } + + _onEditClick(int index, ConsentDetailsModel data) async { + final result = await Navigator.pushNamed( + context, + CustomRouteNames.kConsentAndCapacityAddNewFormScreenRoute, + arguments: ConsentAndCapacityAddNewFormScreenArgs( + userData: widget.userData, + consentData: data, + ), + ); + + if (result is String) { + controller.consentDetailsList[index].description = result; + controller.consentDetailsList.refresh(); + } + // MultilineTextFieldSheet( + // buttonLabel: 'Submit', + // appBarTitle: '', + // textFieldHint: 'Type here...', + // prevValueForField: data.description, + // wantLeadingIcon: false, + // onButtonClick: (String text) { + // controller.updateButtonPressed(text, data.id, index); + // }, + // ).show(); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/currentHealthIssues/current_health_issues_screen.dart b/lib/view/screens/clients/currentHealthIssues/current_health_issues_screen.dart new file mode 100644 index 0000000..58ebb82 --- /dev/null +++ b/lib/view/screens/clients/currentHealthIssues/current_health_issues_screen.dart @@ -0,0 +1,241 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/HealthIssuesDetailsModel.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/enums/body_parts.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/custom_app_bar_with_action.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/human_body_mapper_widget.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/my_circle_image.dart'; +import 'package:get/get.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import '../add_details_to_new_body_point_screen.dart'; +import 'widget/IssueDetailPopupWidget.dart'; + +class CurrentHealthIssuesScreen extends StatefulWidget { + final UserData userData; + + const CurrentHealthIssuesScreen({Key? key, required this.userData}) + : super(key: key); + + @override + State createState() => + _CurrentHealthIssuesScreenState(); +} + +class _CurrentHealthIssuesScreenState extends State { + final CurrentHealthIssuesScreenController controller = + Get.put(CurrentHealthIssuesScreenController()); + + @override + void initState() { + controller.serviceUserId = widget.userData.id!; + + super.initState(); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: controller.tabs.length, + child: CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + onBackButton: () => controller.onBackPress(context), + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: _appBar(context), + body: SingleChildScrollView( + child: Column( + children: [ + //User details + serviceUserDetailWidget(), + + Divider( + height: 1, + color: CustomAppColors.kLightGreyColor, + ), + TabBar( + onTap: (i) { + controller.onTabChange(controller.tabs[i]); + }, + tabs: controller.tabs + .map((e) => Tab( + text: e, + )) + .toList()), + Divider( + height: 1, + color: CustomAppColors.kLightGreyColor, + ), + 8.verticalSpace, + Obx(() { + final entries = controller.issueBodyParts.entries; + + if (entries.isEmpty) { + return FrequentFunctions.noWidget; + } + + final map = {}; + for (var e in entries) { + map[e.key] = e.value.color; + } + + return HumanBodyWidget( + visibleBodyPoints: map, + width: Get.width * 0.9, + onPointTap: (b, p) { + RenderBox? overlay = Overlay.of(context) + .context + .findRenderObject() as RenderBox?; + + showMenu( + context: context, + surfaceTintColor: Colors.white, + position: RelativeRect.fromRect( + p & const Size(40, 40), + // Smaller rect, the touch area + Offset.zero & + (overlay?.size ?? + Get.mediaQuery + .size), // Bigger rect, the entire screen + ), + items: [ + PopupMenuItem( + padding: EdgeInsets.zero, + child: IssueDetailPopupWidget( + userData: widget.userData, + data: controller.issueBodyParts[b]!.data, + onActionChange: (status, data) { + if (data.status != status) { + controller.updateHealthIssueStatus( + bodyPoint: b, data: data, status: status); + } + }, + onUpdateButtonTap: (data) async { + Get.back(); + onAddOrUpdateButtonTap(context, data); + }, + ), + ), + ]); + }, + ); + }), + ], + ), + ), + ), + ); + } + + AppBar _appBar(BuildContext context) { + return CustomAppBarWithAction( + context, + titleText: "Current Health Issues", + actionText: '+ Add New', + onActionTap: () => onAddOrUpdateButtonTap(context), + ); + } + + Widget serviceUserDetailWidget() { + return Padding( + padding: REdgeInsets.symmetric(horizontal: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Align( + alignment: Alignment.center, + child: MyCircleImage( + imageSize: 80.r, + url: + "${WebUrls.baseUrl}${widget.userData.profilePictureUrl ?? ""}", + errorWidget: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + height: 53.r, + width: 53.r, + ), + ), + ), + 10.verticalSpace, + CustomTextWidget( + text: widget.userData.displayName, + fontSize: 14.sp, + fontWeight: FontWeight.w600), + 32.verticalSpace, + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: _labelValueWidgets( + 'Email:', + widget.userData.email ?? "", + ), + )), + 8.horizontalSpace, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: _labelValueWidgets( + 'Contact Number:', + widget.userData.modelId?.phoneNo ?? "", + ), + )), + ], + ), + 16.verticalSpace, + ..._labelValueWidgets( + 'Address 1:', + widget.userData.modelId?.suAddress1 ?? "", + ), + 16.verticalSpace, + ..._labelValueWidgets( + 'Address 2:', + widget.userData.modelId?.suAddress2 ?? "", + ), + 16.verticalSpace, + ], + ), + ); + } + + List _labelValueWidgets(String label, String value) { + return [ + CustomTextWidget( + textAlign: TextAlign.left, + isExpanded: false, + text: label, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kLightTextColor, + fontSize: 12.sp), + 4.verticalSpace, + CustomTextWidget( + textAlign: TextAlign.left, + text: value, + isExpanded: false, + fontWeight: FontWeight.w400, + fontColor: CustomAppColors.kBlackColor, + fontSize: 13.sp), + ]; + } + + Future onAddOrUpdateButtonTap(BuildContext context, + [HealthIssueDetailsModel? data]) async { + dynamic result = await Navigator.of(context) + .pushNamed(CustomRouteNames.kAddDetailsToNewPointScreenRoute, + arguments: AddDetailsToNewBodyPointScreenArgs( + userData: widget.userData, + issueData: data, + )); + if (result == true) { + controller.getPointsDataFromService(); + } + } +} diff --git a/lib/view/screens/clients/currentHealthIssues/widget/IssueDetailPopupWidget.dart b/lib/view/screens/clients/currentHealthIssues/widget/IssueDetailPopupWidget.dart new file mode 100644 index 0000000..de3dce6 --- /dev/null +++ b/lib/view/screens/clients/currentHealthIssues/widget/IssueDetailPopupWidget.dart @@ -0,0 +1,260 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/HealthIssuesDetailsModel.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; + +class IssueDetailPopupWidget extends StatefulWidget { + const IssueDetailPopupWidget({ + super.key, + required this.userData, + required this.data, + required this.onActionChange, + required this.onUpdateButtonTap, + }); + + final UserData userData; + final HealthIssueDetailsModel data; + final Function(bool status, HealthIssueDetailsModel data) onActionChange; + final Function(HealthIssueDetailsModel data) onUpdateButtonTap; + + static const actionActive = "Active"; + static const actionResolved = "Resolved"; + + @override + State createState() => _IssueDetailPopupWidgetState(); +} + +class _IssueDetailPopupWidgetState extends State { + final Map actionsMap = { + IssueDetailPopupWidget.actionActive: true, + IssueDetailPopupWidget.actionResolved: false + }; + + String selectedValue = IssueDetailPopupWidget.actionActive; + + @override + void initState() { + selectedValue = widget.data.status + ? IssueDetailPopupWidget.actionActive + : IssueDetailPopupWidget.actionResolved; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: Get.width, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: double.maxFinite, + padding: REdgeInsets.symmetric(horizontal: 12), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Health Note', + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w400, + ), + ), + CustomTextWidget( + text: widget.data.healthNote, + fontSize: 14.sp, + fontWeight: FontWeight.bold, + textAlign: TextAlign.left, + isExpanded: false), + ], + ), + ), + Container( + width: 90.r, + height: 32.r, + decoration: BoxDecoration( + borderRadius: 24.toRadius(), + color: (selectedValue == + IssueDetailPopupWidget.actionActive) + ? Colors.red.withOpacity(0.3) + : Colors.green.withOpacity(0.3), + ), + child: DropdownButtonHideUnderline( + child: DropdownButtonFormField( + onTap: () { + FocusScopeNode().unfocus(); + }, + value: selectedValue, + dropdownColor: Colors.white, + borderRadius: 4.toRadius(), + decoration: InputDecoration( + isDense: true, + border: InputBorder.none, + suffixIcon: Icon(Icons.arrow_drop_down_sharp, + size: 18, + color: (selectedValue == + IssueDetailPopupWidget.actionActive) + ? Colors.red + : Colors.green) + .paddingOnly(right: 12.r), + suffixIconConstraints: BoxConstraints.tightFor( + width: 24.r, height: 32.r), + // contentPadding: REdgeInsets.only(left: 0), + ), + elevation: 4, + icon: FrequentFunctions.noWidget, + style: TextStyle( + fontWeight: FontWeight.w400, + fontSize: 10.sp, + color: Colors.black), + hint: Text( + "Select...", + style: TextStyle( + fontWeight: FontWeight.w400, + fontSize: 12.sp, + color: CustomAppColors.kLightTextColor, + ), + ), + selectedItemBuilder: (_) { + return actionsMap.keys.map( + (e) { + return DropdownMenuItem( + value: e, + child: Container( + // color: (selectedValue == e) + // ? (selectedValue == + // IssueDetailPopupWidget + // .actionActive) + // ? Colors.red.withOpacity(0.3) + // : Colors.green.withOpacity(0.3) + // : Colors.white, + alignment: Alignment.center, + child: Text( + e, + style: TextStyle( + color: (selectedValue == e) + ? (selectedValue == + IssueDetailPopupWidget + .actionActive) + ? Colors.red + : Colors.green + : Colors.black), + ), + ), + ); + }, + ).toList(); + }, + items: actionsMap.keys + .map( + (e) => DropdownMenuItem( + value: e, + child: Text(e), + ), + ) + .toList(), + // padding: EdgeInsets.zero, + isExpanded: true, + // iconSize: 20.h, + // icon: Padding( + // padding: REdgeInsets.only(right: 0.0), + // child: Icon(Icons.arrow_drop_down_sharp, + // size: 18, + // color: Colors.black), + // ), + onChanged: (v) { + if (v != null) { + setState(() { + selectedValue = v; + widget.onActionChange( + actionsMap[v]!, widget.data); + }); + } + }, + ), + ), + ) + ], + ), + ), + 8.verticalSpace, + Divider( + height: 1, + color: Colors.grey.withOpacity(0.3), + ), + Container( + width: double.maxFinite, + padding: REdgeInsets.symmetric(horizontal: 12, vertical: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich( + textAlign: TextAlign.left, + TextSpan( + children: [ + TextSpan( + text: "Complaint: ", + style: TextStyle( + fontSize: 12.sp, fontWeight: FontWeight.w600), + ), + TextSpan( + text: widget.data.complaint, + style: TextStyle( + fontSize: 12.sp, fontWeight: FontWeight.w400), + ), + ], + ), + ), + 4.verticalSpace, + Text.rich( + textAlign: TextAlign.left, + TextSpan( + children: [ + TextSpan( + text: "Last Update: ", + style: TextStyle( + fontSize: 12.sp, fontWeight: FontWeight.w600), + ), + TextSpan( + text: DateFormat("MMM/dd/yyyy").format( + DateTime.parse(widget.data.updatedAt) + .toLocal()), + style: TextStyle( + fontSize: 12.sp, fontWeight: FontWeight.w400), + ), + ], + ), + ), + ], + ), + ), + 16.verticalSpace, + Center( + child: SizedBox( + width: 120.r, + height: 32.r, + child: CustomAppButton( + onTap: () => widget.onUpdateButtonTap(widget.data), + buttonText: "Update", + ), + ), + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/view/screens/clients/document_details_screen.dart b/lib/view/screens/clients/document_details_screen.dart new file mode 100644 index 0000000..7415ac0 --- /dev/null +++ b/lib/view/screens/clients/document_details_screen.dart @@ -0,0 +1,363 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; + +class DocumentDetailsScreen extends StatefulWidget { + const DocumentDetailsScreen({Key? key}) : super(key: key); + + @override + State createState() => _DocumentDetailsScreenState(); +} + +class _DocumentDetailsScreenState extends State { + final DocumentDetailsScreenController controller = + Get.put(DocumentDetailsScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'Documents', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + const Spacer(), + InkWell( + onTap: () async { + // await Navigator.pushNamed( + // controller.screenKey.currentContext!, + // CustomRouteNames.kNewNoteScreenRoute, + // ); + }, + child: CustomTextWidget( + text: '+ Add New', + isExpanded: false, + fontSize: 14.sp, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kLightTextColor, + ), + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: SingleChildScrollView( + child: Column( + children: [ + // ClipRRect( + // borderRadius: BorderRadius.circular(50.r), + // child: CustomImageWidget( + // imagePath: FrequentFunctions + // .userModel.value.profilePictureUrl.isNotEmpty + // ? "${WebUrls.baseUrl}${FrequentFunctions.userModel.value.profilePictureUrl}" + // : AssetsManager.kPersonMainIcon, + // imageColor: FrequentFunctions + // .userModel.value.profilePictureUrl.isNotEmpty + // ? null + // : CustomAppColors.kLightTextColor, + // height: 80.h, + // width: 80.w, + // ), + // ), + SizedBox(height: 16.h), + CustomTextWidget( + text: 'user', fontSize: 14.sp, fontWeight: FontWeight.w600), + SizedBox(height: 16.h), + const Row( + children: [ + Expanded( + child: BuildDetailSingleItem( + title: 'Email', + value: "jaylon.n@ftcservices.com", + )), + Expanded( + child: BuildDetailSingleItem( + title: 'Contact Number', + value: "+44 (0) 00 0000 0000", + )), + ], + ), + const Row( + children: [ + Expanded( + child: BuildDetailSingleItem( + title: 'NI Number', + value: "QQ 123456 C", + )), + Expanded( + child: BuildDetailSingleItem( + title: 'DOB', value: "15/11/1996")), + ], + ), + const Row( + children: [ + Expanded( + child: BuildDetailSingleItem( + title: 'Post Code', + value: "GL55 8PN", + )), + Expanded( + child: BuildDetailSingleItem( + title: 'Kin', value: "12PO025")), + ], + ), + const Row( + children: [ + BuildDetailSingleItem( + title: 'Address', + value: "Gloucester, 1-2 Street Name", + ), + ], + ), + SizedBox(height: 16.h), + Container( + height: 30, + margin: EdgeInsets.symmetric(horizontal: 5.w, vertical: 10.h), + decoration: BoxDecoration( + border: Border.all( + color: CustomAppColors.kDarkBlueTextColor, + ), + borderRadius: BorderRadius.circular(5.r), + ), + child: Row( + children: [ + Container( + padding: const EdgeInsets.only(left: 5, right: 10), + child: const Center( + child: Icon(Icons.search), + ), + ), + CustomTextWidget( + text: "Search...", + fontSize: 18.sp, + fontWeight: FontWeight.w400, + fontColor: CustomAppColors.kLightGreyColor, + isExpanded: false, + ), + ], + ), + ), + SizedBox(height: 8.h), + SizedBox( + width: MediaQuery.of(context).size.width, + child: Column( + // crossAxisAlignment: CrossAxisAlignment.center, + children: [ + listHeading(), + listItemWidget( + text1: "Health Report and data", + text3: "Aug/01/2024", + color: false), + listItemWidget( + text1: "Health Report and data", + text3: "Sept/01/2024", + color: false), + listItemWidget( + text1: "Health Report and data", + text3: "Aug/01/2024", + color: false), + listItemWidget( + text1: "Health Report and data", + text3: "Sept/01/2024", + color: false), + listItemWidget( + text1: "Health Report and data", + text3: "Aug/01/2024", + color: false), + listItemWidget( + text1: "Health Report and data", + text3: "Sept/01/2024", + color: false), + listItemWidget( + text1: "Health Report and data", + text3: "Aug/01/2024", + color: false), + listItemWidget( + text1: "Health Report and data", + text3: "Sept/01/2024", + color: false), + listItemWidget( + text1: "Health Report and data", + text3: "Aug/01/2024", + color: false), + listItemWidget( + text1: "Health Report and data", + text3: "Sept/01/2024", + color: false), + ], + ), + ) + ], + ), + ), + ), + ); + } + + Widget listHeading() { + return Container( + decoration: const BoxDecoration( + border: Border( + top: BorderSide( + color: CustomAppColors.kSmokeColor, + ), + bottom: BorderSide(color: CustomAppColors.kSmokeColor), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 2, + child: Container( + padding: const EdgeInsets.only(right: 0, top: 10, bottom: 10), + child: const CustomTextWidget( + textAlign: TextAlign.left, + text: "All Documents", + isExpanded: false, + fontWeight: FontWeight.w700), + ), + ), + Expanded( + flex: 1, + child: Container( + padding: const EdgeInsets.only(right: 0, top: 10, bottom: 10), + child: CustomTextWidget( + text: "+ Add New", + isExpanded: false, + fontWeight: FontWeight.w500, + fontColor: CustomAppColors.kLightGreyColor), + ), + ), + ], + ), + ); + } + + Widget listItemWidget( + {required String text1, required String text3, required bool color}) { + return Container( + decoration: BoxDecoration( + color: color ? CustomAppColors.kBlueColor.withAlpha(20) : null, + border: const Border( + top: BorderSide( + color: CustomAppColors.kSmokeColor, + ), + bottom: BorderSide(color: CustomAppColors.kSmokeColor), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 2, + child: Container( + padding: const EdgeInsets.only(top: 10, bottom: 10), + child: Row( + children: [ + const CustomImageWidget(imagePath: AssetsManager.kFolderIcon), + SizedBox( + width: 5.sp, + ), + Container( + padding: const EdgeInsets.only( + left: 5, + ), + child: CustomTextWidget( + text: text1, + textAlign: TextAlign.left, + fontSize: 11.sp, + isExpanded: false, + ), + ), + ], + ), + ), + ), + Expanded( + flex: 1, + child: Container( + padding: const EdgeInsets.only(top: 10, bottom: 10, left: 3), + child: CustomTextWidget( + text: text3, + fontSize: 11.sp, + isExpanded: false, + ), + ), + ), + ], + ), + ); + } +} + +class BuildDetailSingleItem extends StatelessWidget { + const BuildDetailSingleItem({ + super.key, + required this.title, + required this.value, + }); + + final String title; + final String value; + + @override + Widget build(BuildContext context) { + return Container( + height: 52.h, + child: Padding( + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 5.w), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTextWidget( + textAlign: TextAlign.left, + isExpanded: false, + text: '$title: ', + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kLightTextColor, + fontSize: 12.sp), + const Spacer(), + CustomTextWidget( + textAlign: TextAlign.left, + text: value, + isExpanded: false, + fontWeight: FontWeight.w400, + fontColor: CustomAppColors.kBlackColor, + fontSize: + title.toLowerCase().contains("email") ? 11.sp : 13.sp), + ], + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/documentsList/documents_list_screen.dart b/lib/view/screens/clients/documentsList/documents_list_screen.dart new file mode 100644 index 0000000..3a2baa6 --- /dev/null +++ b/lib/view/screens/clients/documentsList/documents_list_screen.dart @@ -0,0 +1,202 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/documents_list_model.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/custom_app_bar_with_action.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/edit_icon.dart'; +import 'package:ftc_mobile_app/view/screens/clientsListing/widgets/search_bar_widget.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; + +class DocumentsListScreen extends StatefulWidget { + final UserData userData; + + const DocumentsListScreen({Key? key, required this.userData}) + : super(key: key); + + @override + State createState() => _DocumentsListScreenState(); +} + +class _DocumentsListScreenState extends State { + late final DocumentsListScreenController controller; + + @override + void initState() { + controller = Get.put(DocumentsListScreenController(widget.userData)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + appBar: _appBar(context), + body: Column( + children: [ + SearchBarWidget( + controller: controller.searchTEC, + onSearchTextChange: controller.onSearch, + ), + Expanded( + child: Obx( + () { + return controller.documentsList.isEmpty + ? FrequentFunctions.centerText(text: "No data found") + : listView(); + }, + ), + ), + ], + ), + ); + } + + AppBar _appBar(BuildContext context) { + return CustomAppBarWithAction( + context, + titleText: "Documents", + actionText: '+ Add New', + onActionTap: () async { + dynamic response = await Navigator.pushNamed( + context, CustomRouteNames.kAddNewDocumentScreenRoute, + arguments: controller.serviceUser.value); + if (response is DocumentModel) { + controller.documentsList.insert(0, response); + controller.documentsList.refresh(); + } + }, + ); + } + + Widget _tableHeading(String text) { + return TableCell( + child: Padding( + padding: REdgeInsets.symmetric(horizontal: 12, vertical: 12), + child: Text( + text, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w600, + ), + ), + ), + ); + } + + Widget listView() { + return InteractiveViewer( + constrained: false, + scaleEnabled: false, + child: SizedBox( + width: Get.width + 100.r, + child: Table( + border: TableBorder.all( + color: CustomAppColors.kSmokeColor, + ), + columnWidths: const { + 0: FlexColumnWidth(30.2), + 1: FlexColumnWidth(35.0), + 2: FlexColumnWidth(25.0), + // 3: FlexColumnWidth(15.0), + }, + defaultVerticalAlignment: TableCellVerticalAlignment.middle, + children: [ + TableRow( + children: [ + _tableHeading("Document Name"), + _tableHeading("Details"), + _tableHeading("Date"), + // _tableHeading("Actions"), + ], + ), + ...controller.documentsList + .map((e) => tableRow(controller.documentsList.indexOf(e), e)) + .toList() + ], + ), + ), + ); + } + + TableRow tableRow(index, DocumentModel e) { + return TableRow( + decoration: BoxDecoration( + color: controller.documentsList.indexOf(e) % 2 == 0 + ? CustomAppColors.kLightGreyColor.withOpacity(0.25) + : Colors.white), + children: [ + Padding( + padding: REdgeInsets.symmetric(vertical: 8.0, horizontal: 12), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const CustomImageWidget(imagePath: AssetsManager.kFolderIcon), + Expanded( + child: Container( + padding: const EdgeInsets.only(left: 5), + child: CustomTextWidget( + text: e.title, + textAlign: TextAlign.left, + fontSize: 11.sp, + isExpanded: false, + ), + ), + ), + ], + ), + ), + Padding( + padding: REdgeInsets.symmetric(vertical: 8.0, horizontal: 12), + child: CustomTextWidget( + text: e.details, + textAlign: TextAlign.left, + fontSize: 11.sp, + isExpanded: false, + ), + ), + Padding( + padding: REdgeInsets.symmetric(vertical: 8.0, horizontal: 12), + child: CustomTextWidget( + text: DateFormat("MMM/dd/yyyy").format( + DateTime.tryParse(e.createdAt)?.toLocal() ?? DateTime.now()), + textAlign: TextAlign.left, + fontSize: 11.sp, + isExpanded: false, + ), + ), + // Padding( + // padding: REdgeInsets.symmetric(vertical: 8.0, horizontal: 12), + // child: Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // EditIcon( + // onTap: () => _onEditTap(index, e), + // ), + // ], + // ), + // ) + ], + ); + } + + // _onEditTap(int index, documentModel) async { + // var response = await Navigator.pushNamed( + // context, CustomRouteNames.kAddNewDocumentScreenRoute, + // arguments: [documentModel, controller.serviceUser()]); + // if (response is DocumentModel) { + // // int index = controller.documentsList.value.documentList.indexWhere((item) => item == documentModel); + // controller.documentsList[index] + // ..title = response.title + // ..details = response.details + // ..docPath = response.docPath; + // controller.documentsList.refresh(); + // } + // } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/export_clients_module.dart b/lib/view/screens/clients/export_clients_module.dart new file mode 100644 index 0000000..734ebb4 --- /dev/null +++ b/lib/view/screens/clients/export_clients_module.dart @@ -0,0 +1,22 @@ +export '../clientsListing/clients_list_screen.dart'; +export 'client_profile_screen.dart'; +export 'appointments_screen.dart'; +export 'notes_screen.dart'; +export 'select_note_screen.dart'; +export 'new_note_screen.dart'; +export 'care_plan_menu_screen.dart'; +export 'documentsList/documents_list_screen.dart'; +export 'document_details_screen.dart'; +export 'recent_incidents_screen.dart'; +export 'currentHealthIssues/current_health_issues_screen.dart'; +export 'consent_and_capacity_add_new_form_screen.dart'; +export 'consent_and_capacity_questionnaire_screen.dart'; +export 'life_history_and_goals_screen.dart'; +export 'risk_assessments_screen.dart'; +export 'pbs_plan_screen.dart'; +export 'photo_gallery_screen.dart'; +export 'risk_assessments_template_screen.dart'; +export 'clients_new_view_module/export_client_new_view.dart'; +export 'care_notes_screen.dart'; +export 'care_notes_subcategories_screen.dart'; +export 'careNoteForms/export_care_note_forms.dart'; \ No newline at end of file diff --git a/lib/view/screens/clients/life_history_and_goals_screen.dart b/lib/view/screens/clients/life_history_and_goals_screen.dart new file mode 100644 index 0000000..17e1bf4 --- /dev/null +++ b/lib/view/screens/clients/life_history_and_goals_screen.dart @@ -0,0 +1,151 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/clients/export_clients_controllers.dart'; +import 'package:get/get.dart'; + +import '../../../ftc_mobile_app.dart'; + +class LifeHistoryAndGoalsScreen extends StatefulWidget { + const LifeHistoryAndGoalsScreen({Key? key}) : super(key: key); + + @override + State createState() => _LifeHistoryAndGoalsScreenState(); +} + +class _LifeHistoryAndGoalsScreenState extends State { + + LifeHistoryAndGoalsScreenController controller = Get.put(LifeHistoryAndGoalsScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'Life History & Goals ', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: SingleChildScrollView( + child: Column( + children: [ + CustomTextWidget(text: "Questionnaire",isExpanded: false,fontWeight: FontWeight.w600,fontSize: 14.sp,textAlign: TextAlign.left,alignment: Alignment.topLeft), + SizedBox(height: 22.h), + LifeHistoryQuestionCard(imagePath: AssetsManager.kManImagePng,name: "Leo Hurwitz",question: "Q: If there was one thing you could change about your health, what would it be?",date: "Aug/02/2023/7:30pm"), + LifeHistoryAnswerCard(imagePath: AssetsManager.kPersonMainIcon,name: "Leo Hurwitz",answer: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.",date: "Aug/02/2023/7:30pm"), + LifeHistoryQuestionCard(imagePath: AssetsManager.kManImagePng,name: "Leo Hurwitz",question: "Q: If there was one thing you could change about your health, what would it be?",date: "Aug/02/2023/7:30pm"), + LifeHistoryAnswerCard(imagePath: AssetsManager.kPersonMainIcon,name: "Leo Hurwitz",answer: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.",date: "Aug/02/2023/7:30pm"), + LifeHistoryQuestionCard(imagePath: AssetsManager.kManImagePng,name: "Leo Hurwitz",question: "Q: If there was one thing you could change about your health, what would it be?",date: "Aug/02/2023/7:30pm"), + LifeHistoryAnswerCard(imagePath: AssetsManager.kPersonMainIcon,name: "Leo Hurwitz",answer: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.",date: "Aug/02/2023/7:30pm"), + ], + ), + ), + ), + ); + } +} + +class LifeHistoryQuestionCard extends StatelessWidget { + const LifeHistoryQuestionCard({ + super.key, required this.imagePath, required this.name, required this.question, required this.date, + }); + + final String imagePath; + final String name; + final String question; + final String date; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + CustomImageWidget(imagePath: imagePath,width: 32.w,height: 32.h,), + SizedBox(width: 12.w,), + CustomTextWidget(text: name,isExpanded: false), + ], + ), + SizedBox(height: 8.h), + CustomTextWidget(text: question,isExpanded: false,fontWeight: FontWeight.bold,textAlign: TextAlign.left,alignment: Alignment.centerLeft,fontSize: 14.sp), + SizedBox(height: 8.h), + CustomTextWidget(text: date,isExpanded: false,textAlign: TextAlign.left,alignment: Alignment.centerLeft,fontSize: 12.sp), + SizedBox(height: 24.h), + ], + ); + } +} + +class LifeHistoryAnswerCard extends StatelessWidget { + const LifeHistoryAnswerCard({ + super.key, required this.imagePath, required this.name, required this.answer, required this.date, + }); + + final String imagePath; + final String name; + final String answer; + final String date; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + CustomImageWidget(imagePath: imagePath,width: 32.w,height: 32.h,), + SizedBox(width: 12.w,), + CustomTextWidget(text: name,isExpanded: false), + const Spacer(), + CustomImageWidget(imagePath: AssetsManager.kPencilOutlineIcon,width: 14.w,height: 14.h,), + ], + ), + SizedBox(height: 8.h), + Text.rich( + TextSpan( + children: [ + const TextSpan( + text: 'A: ', + style: TextStyle(fontWeight: FontWeight.bold), + ), + TextSpan(style: TextStyle(fontSize: 14.sp,color: CustomAppColors.kBlackColor.withAlpha(180),),text: answer), + ], + ), + ), + SizedBox(height: 8.h), + CustomTextWidget(text: date,isExpanded: false,textAlign: TextAlign.left,alignment: Alignment.centerLeft,fontSize: 12.sp), + SizedBox(height: 24.h), + ], + ); + } +} diff --git a/lib/view/screens/clients/new_note_screen.dart b/lib/view/screens/clients/new_note_screen.dart new file mode 100644 index 0000000..b07974e --- /dev/null +++ b/lib/view/screens/clients/new_note_screen.dart @@ -0,0 +1,364 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../../ftc_mobile_app.dart'; + +class NewNoteScreen extends StatefulWidget { + const NewNoteScreen({Key? key}) : super(key: key); + + @override + State createState() => _NewNoteScreenState(); +} + +class _NewNoteScreenState extends State { + NewNoteScreenController controller = Get.put(NewNoteScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: 'New Note for ${controller.user.name}', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 18.w), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + + Padding( + padding: EdgeInsets.only(top: 20.h,bottom: 15.h), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + minLines: 1, + maxLines: 1, + controller: controller.titleController, + hintText: ConstantText.kTypeTitle, + heading: ConstantText.kTitle, + onChange: (_){}, + ), + ), + + Padding( + padding: EdgeInsets.only(bottom: 15.0.h), + child: const UploadWidget(), + ), + Row( + children: [ + Expanded(child: Padding( + padding: EdgeInsets.only(right: 7.5.w), + child: const FlagWidget(text1: "Flag",text2: "Choose Flag",flagIcon: AssetsManager.kFlagIcon), + )), + Expanded(child: Padding( + padding: EdgeInsets.only(left: 7.5.w), + child: const FlagWidget(text1: "Red Flag: How long?",text2: "Choose",chooseIcon: AssetsManager.kClockIcon), + )), + ], + ), + Padding( + padding: EdgeInsets.only(top: 20.h,bottom: 15.h), + child: CustomTextFieldWidget( + borderRadius: BorderRadius.circular(10.r), + borderColor: CustomAppColors.kLightGreyColor, + borderWidth: 1.0.sp, + maxLines: 6, + minLines: 6, + controller: controller.titleController, + hintText: ConstantText.kTypeTitle, + heading: ConstantText.kTitle, + onChange: (_){}, + bottomChild: Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0.w,bottom: 10.h), + child: InkWell( + onTap: () {}, + child: const CustomImageWidget( + imagePath:AssetsManager.kMicIcon, + ), + ), + ), + ), + ), + ), + const TitleWidget( + text1: "Link Note (Optional)", + text2: "Choose", + showDropDownButton: true, + showSwitchButton: false), + Padding( + padding: EdgeInsets.symmetric(vertical: 15.0.h), + child: const TitleWidget( + text1: "Handover To (Optional)", + text2: "Choose", + showDropDownButton: true, + showSwitchButton: false), + ), + const TitleWidget( + text1: "Notify Management?", + text2: "No", + showDropDownButton: false, + showSwitchButton: true), + // const SubmitButtonWidget( + // text: "Submit", + // buttonColor: CustomAppColors.kSecondaryColor, + // textColor: CustomAppColors.kPrimaryColor, + // ), + + + Padding( + padding: EdgeInsets.only( + top: 15.h, + bottom: Platform.isIOS ? 30.0.h : 20.0.h, + ), + child: CustomAppButton( + buttonText: ConstantText.kSubmit.toUpperCase(), + buttonColor: CustomAppColors.kSecondaryColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: (){}, + ), + ), + ], + ), + ), + ); + } +} + +class UploadWidget extends StatelessWidget { + const UploadWidget({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width, + // height: 50, + padding: EdgeInsets.symmetric(vertical: 12.h,), + // margin: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h), + decoration: BoxDecoration( + border: Border.all( + color: CustomAppColors.kLightGreyColor, + ), + borderRadius: BorderRadius.circular(8.r)), + child: Column( + children: [ + CustomImageWidget( + imagePath: AssetsManager.kUploadIcon, + height: 24.h, + width: 24.w, + ), + CustomTextWidget( + text: "Upload Image", + fontSize: 14.sp, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kLightTextColor, + isExpanded: false), + ], + ), + ); + } +} + +class TitleWidget extends StatelessWidget { + const TitleWidget({ + super.key, + required this.text1, + required this.text2, + required this.showDropDownButton, + required this.showSwitchButton, + }); + + final String text1; + final String text2; + final bool showDropDownButton; + final bool showSwitchButton; + + @override + Widget build(BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.all(10.sp,), + decoration: BoxDecoration( + border: Border.all( + color: CustomAppColors.kLightGreyColor, + ), + borderRadius: BorderRadius.circular(8.r)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + CustomTextWidget( + text: text1, + fontSize: 10.sp, + fontWeight: FontWeight.w500, + fontColor: CustomAppColors.kLightTextColor, + isExpanded: false), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CustomTextWidget( + text: text2, + fontSize: 14.sp, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kDarkBlueTextColor, + isExpanded: false), + showDropDownButton + ? const Icon(Icons.arrow_drop_down_outlined) + : showSwitchButton + ? SizedBox( + height: 20.h, + width: 48.w, + child: Switch( + inactiveTrackColor: CustomAppColors.kPrimaryColor, + activeTrackColor: CustomAppColors.kSecondaryColor, + value: false, + onChanged: (val) {}), + ) + : Container(), + ], + ), + ], + ), + ); + } +} + +class SubmitButtonWidget extends StatelessWidget { + const SubmitButtonWidget({ + super.key, + required this.text, + required this.textColor, + required this.buttonColor, + this.borderColor, + }); + + final String text; + final Color textColor; + final Color buttonColor; + final Color? borderColor; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () {}, + child: Container( + width: MediaQuery.of(context).size.width, + alignment: Alignment.center, + padding: EdgeInsets.symmetric(vertical: 10.h), + decoration: BoxDecoration( + border: borderColor != null ? Border.all(color: borderColor!) : null, + color: buttonColor, + borderRadius: BorderRadius.circular(2.r), + ), + child: CustomTextWidget( + text: text, + fontColor: textColor, + fontSize: 14.sp, + fontWeight: FontWeight.w700, + ), + ), + ); + } +} + +class FlagWidget extends StatelessWidget { + const FlagWidget({ + super.key, + required this.text1, + required this.text2, + this.flagIcon, + this.chooseIcon, + }); + + final String text1; + final String text2; + final String? flagIcon; + final String? chooseIcon; + + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(left: 10.w,right: 10.w,top: 5.h,bottom: 5.h), + decoration: BoxDecoration( + border: Border.all( + color: CustomAppColors.kLightGreyColor, + ), + borderRadius: BorderRadius.circular(10.r)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(bottom: 6.0.h), + child: CustomTextWidget( + text: text1, + fontSize: 10.sp, + fontWeight: FontWeight.w500, + fontColor: CustomAppColors.kLightTextColor, + isExpanded: false), + ), + Row( + children: [ + flagIcon!=null? + CustomImageWidget( + imagePath: flagIcon!, + height: 18.66.h, + width: 18.w, + ): Container(), + + Expanded( + child: CustomTextWidget( + alignment: Alignment.centerLeft, + text: text2, + fontSize: 14.sp, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kDarkBlueTextColor, + isExpanded: false), + ), + + chooseIcon!=null? + CustomImageWidget( + imagePath: chooseIcon!, + height: 18.66.h, + width: 18.w, + ): Container(), + ], + ), + ], + ), + ); + } +} diff --git a/lib/view/screens/clients/notes_screen.dart b/lib/view/screens/clients/notes_screen.dart new file mode 100644 index 0000000..699f404 --- /dev/null +++ b/lib/view/screens/clients/notes_screen.dart @@ -0,0 +1,209 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../ftc_mobile_app.dart'; + +class NotesScreen extends StatefulWidget { + const NotesScreen({Key? key}) : super(key: key); + + @override + State createState() => _NotesScreenState(); +} + +class _NotesScreenState extends State { + NotesScreenController controller = + Get.put(NotesScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + Navigator.pushNamed( + controller.screenKey.currentContext!, + CustomRouteNames.kNewNoteScreenRoute, + ); + }, + shape: const CircleBorder(), + backgroundColor: CustomAppColors.kPrimaryColor, + child: CustomImageWidget( + imagePath: AssetsManager.kPlusIcon, + height: 35.h, + width: 35.w, + imageColor: CustomAppColors.kSecondaryColor, + ), + ), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox(width: 15.w,), + CustomTextWidget( + text: 'Notes for ${controller.user.name}', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + ), + body: Center( + child: SingleChildScrollView( + child: Column( + children: [ + Container( + height: 40, + margin: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h), + decoration: BoxDecoration( + border: Border.all( + color: CustomAppColors.kLightGreyColor, + ), + borderRadius: BorderRadius.circular(40.r)), + child: Row( + children: [ + IconButton( + onPressed: () { + Navigator.pushNamed(context, CustomRouteNames.kSelectNoteScreenRoute); + }, icon: const Icon(Icons.search)), + CustomTextWidget( + text: "Search...", + fontSize: 18.sp, + fontWeight: FontWeight.w400, + fontColor: CustomAppColors.kLightGreyColor, + isExpanded: false), + ], + ), + ), + SizedBox( + height: MediaQuery.of(context).size.height / 1.2, + child: ListView.builder( + itemCount: controller.users.length + 1, + itemBuilder: (BuildContext context, int index) { + return index < controller.users.length + ? NotesRoundOutlinedBox( + context: context, + child: BuildNotesList( + history: controller.user.aboutPatient, + date: controller.user.diagnosisDate, + userName: controller.users[index], + ), + ) + : SizedBox(height: 60.h,); + }, + ), + ), + ], + ), + ), + ), + ); + } +} + +class NotesRoundOutlinedBox extends StatelessWidget { + const NotesRoundOutlinedBox({ + super.key, + required this.context, + required this.child, + }); + + final BuildContext context; + final Widget child; + + @override + Widget build(BuildContext context) { + return Container( + + alignment: Alignment.centerLeft, + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.symmetric(horizontal: 25.w,vertical: 5.h), + padding: EdgeInsets.symmetric(horizontal: 13.sp,vertical: 10), + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kSecondaryColor), + borderRadius: BorderRadius.circular(10.r), + ), + child: child, + ); + } +} + +class BuildNotesList extends StatelessWidget { + const BuildNotesList({ + super.key, + required this.date, + required this.history, required this.userName, + }); + + final String date; + final String history; + final String userName; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTextWidget( + text: 'Note Title', + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + CustomTextWidget( + text: date, + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 10.sp, + fontColor: CustomAppColors.kLightGreyColor, + ), + CustomTextWidget( + alignment: Alignment.centerLeft, + textAlign: TextAlign.left, + text: history, + isExpanded: false, + fontSize: 10.sp, + fontColor: CustomAppColors.kBlackColor, + ), + Padding( + padding: EdgeInsets.only(top: 8.0.h), + child: Row( + children: [ + CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + height: 18.h, + width: 18.w, + ), + SizedBox(width: 8.w,), + CustomTextWidget( + alignment: Alignment.centerLeft, + text: userName, + isExpanded: false, + fontSize: 10.sp, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kBlackColor, + ), + ], + ), + ), + ], + ); + } +} \ No newline at end of file diff --git a/lib/view/screens/clients/pbs_plan_screen.dart b/lib/view/screens/clients/pbs_plan_screen.dart new file mode 100644 index 0000000..b1771d8 --- /dev/null +++ b/lib/view/screens/clients/pbs_plan_screen.dart @@ -0,0 +1,192 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/PBSPlanModel.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/custom_app_bar_with_action.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/my_circle_image.dart'; +import 'package:ftc_mobile_app/view/screens/clients/clients_new_view_module/add_new_pbs_plan_screen.dart'; +import 'package:get/get.dart'; +import '../../../ftc_mobile_app.dart'; + +class PBSPlanScreen extends StatefulWidget { + final UserData userData; + + const PBSPlanScreen({Key? key, required this.userData}) : super(key: key); + + @override + State createState() => _PBSPlanScreenState(); +} + +class _PBSPlanScreenState extends State { + final controller = Get.put(PBSPlanScreenController()); + + @override + void initState() { + controller.serviceUser.value = widget.userData; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + // onBackButton: () => controller.onBackPress(context), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + // sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: _appBar(context), + body: Column( + children: [ + // SearchBarWidget(onSearchTextChange: (_) {}), + Expanded(child: Obx(() { + if (controller.pbsList.isEmpty) { + return FrequentFunctions.centerText(text: "No data found"); + } + + return listView(controller.pbsList()); + })), + ], + ), + ); + } + + AppBar _appBar(BuildContext context) { + return CustomAppBarWithAction( + context, + titleText: "PBS Plan", + actionText: '+ Add Form', + onActionTap: _onAddNewTap, + ); + } + + Widget listView(List list) { + return DecoratedBox( + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kLightGreyColor)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: REdgeInsets.symmetric(horizontal: 16.0, vertical: 12), + child: Text( + "Staff Member", + style: TextStyle( + color: CustomAppColors.kBlackColor, + fontSize: 14.sp, + fontWeight: FontWeight.w600, + ), + ), + ), + Expanded( + child: ListView.separated( + itemCount: list.length, + separatorBuilder: (_, index) => Divider( + color: CustomAppColors.kLightGreyColor, + height: 1, + ), + itemBuilder: (BuildContext context, int index) { + return InkWell( + onTap: () { + // listItemTap(index, list[index]); + }, + child: listItem(index: index, data: list[index]), + ); + }, + ), + ), + ], + ), + ); + } + + Widget listItem({required int index, required PbsList data}) { + return InkWell( + onTap: () async { + Navigator.pushNamed( + context, + CustomRouteNames.kAddNewPBSPlanScreenRoute, + arguments: AddNewPBSPlanScreenArgs( + userData: widget.userData, pbsData: data, viewOnly: true), + ); + }, + child: Container( + padding: REdgeInsets.symmetric(vertical: 9, horizontal: 16), + color: index % 2 == 0 + ? CustomAppColors.kLightGreyColor.withOpacity(0.1) + : Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + MyCircleImage( + imageSize: 53.r, + url: "${WebUrls.baseUrl}${data.staffId?.profilePictureUrl ?? ""}", + errorWidget: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + imageColor: CustomAppColors.kDarkBlueTextColor, + height: 53.r, + width: 53.r, + ), + ), + 12.horizontalSpace, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + data.staffId?.name ?? "", + style: TextStyle( + color: CustomAppColors.kDarkBlueTextColor, + fontSize: 16.sp, + fontWeight: FontWeight.w600, + ), + ), + (data.createdAt.isNullOrEmpty()) + ? FrequentFunctions.noWidget + : Text( + DateFormatter.ddMMyyyyhhmmFormat( + DateTime.parse(data.createdAt).toLocal()), + style: TextStyle( + color: CustomAppColors.kLightGreyColor, + fontSize: 12.sp, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + // EditIcon(onTap: () async { + // dynamic res = await Navigator.pushNamed( + // context, CustomRouteNames.kAddNewPBSPlanScreenRoute, + // arguments: AddNewPBSPlanScreenArgs( + // userData: widget.userData, pbsData: data)); + // if (res == true) { + // controller.fetchPBSPlanList(); + // } + // }), + ], + ), + ), + ); + } + + _onAddNewTap() async { + dynamic res = await Navigator.pushNamed( + controller.screenKey.currentContext!, + CustomRouteNames.kAddNewPBSPlanScreenRoute, + arguments: AddNewPBSPlanScreenArgs( + userData: widget.userData, + )); + if (res == true) { + controller.fetchPBSPlanList(); + } + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/photo_gallery_screen.dart b/lib/view/screens/clients/photo_gallery_screen.dart new file mode 100644 index 0000000..14e15e3 --- /dev/null +++ b/lib/view/screens/clients/photo_gallery_screen.dart @@ -0,0 +1,234 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/memoryListResponse/MemoryListData.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/custom_app_bar_with_action.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/my_network_image.dart'; +import 'package:get/get.dart'; +import 'package:video_thumbnail/video_thumbnail.dart'; +import '../../../ftc_mobile_app.dart'; +import 'addEditMemoryBox/add_edit_memory_box_screen.dart'; + +class PhotoGalleryScreen extends StatefulWidget { + final UserData userData; + + const PhotoGalleryScreen({Key? key, required this.userData}) + : super(key: key); + + @override + State createState() => _PhotoGalleryScreenState(); +} + +class _PhotoGalleryScreenState extends State { + final controller = Get.put(PhotoGalleryScreenController()); + + @override + void initState() { + controller.serviceUserId = widget.userData.id!; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + // sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: _appBar(context), + body: SafeArea(child: Obx(() { + if (controller.memoryList.isEmpty) { + return FrequentFunctions.centerText(text: "No data found"); + } + + return listView(controller.memoryList()); + })), + ); + } + + AppBar _appBar(BuildContext context) { + return CustomAppBarWithAction( + context, + titleText: 'Photo Gallery', + actionText: '+ Add New', + onActionTap: () async { + final result = await _gotoAddEditMemoryBoxScreen(); + + if (result == true) { + controller.getMemoryList(); + } + }, + ); + } + + listView(List list) { + return ListView.separated( + itemCount: list.length, + padding: REdgeInsets.symmetric(horizontal: 16), + separatorBuilder: (_, index) => 12.verticalSpace, + itemBuilder: (BuildContext context, int index) { + return listItem(index: index, data: list[index]); + }, + ); + } + + Widget listItem({required int index, required MemoryListData data}) { + return InkWell( + onTap: () { + _gotoAddEditMemoryBoxScreen(data, true); + }, + child: Container( + height: 130.h, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: CustomAppColors.kSecondaryColor), + borderRadius: 16.toRadius(), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 4, + child: Container( + width: double.maxFinite, + height: double.maxFinite, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Colors.grey.withOpacity(0.2), + borderRadius: + const BorderRadius.horizontal(left: Radius.circular(16)), + ), + child: (data.filePath.isNullOrEmpty()) + ? const Center( + child: Icon(Icons.image_not_supported_outlined), + ) + : (data.filePath!.isImageFileName) + ? MyNetworkImage( + url: WebUrls.baseUrl + data.filePath!, + fit: BoxFit.cover, + errorWidget: const Icon( + Icons.image_not_supported_outlined, + color: Colors.black, + ), + ) + : FutureBuilder( + future: VideoThumbnail.thumbnailFile( + video: WebUrls.baseUrl + data.filePath!, + imageFormat: ImageFormat.WEBP, + maxHeight: 150, + quality: 75, + ), + builder: (_, snap) { + if (snap.connectionState == + ConnectionState.waiting) { + return Center( + child: SizedBox.square( + dimension: 32.r, + child: const CircularProgressIndicator(), + ), + ); + } + if (snap.connectionState == + ConnectionState.done) { + if (snap.data != null) { + return Stack( + children: [ + Positioned.fill( + child: Image.file( + File(snap.data!), + fit: BoxFit.cover, + ), + ), + Center( + child: DecoratedBox( + decoration: BoxDecoration( + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + blurRadius: 2, + spreadRadius: 2, + color: Colors.black + .withOpacity(0.2)), + ], + ), + child: Icon( + Icons.play_circle_outline_rounded, + color: Colors.white, + size: 32.r, + ), + ), + ) + ], + ); + } + } + + return FrequentFunctions.noWidget; + }), + ), + ), + 12.horizontalSpace, + Expanded( + flex: 8, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + CustomTextWidget( + text: data.note ?? "", + fontSize: 14.sp, + fontWeight: FontWeight.w400, + maxLines: 3, + textAlign: TextAlign.left, + ), + 8.verticalSpace, + CustomTextWidget( + text: "Staff Member: ${data.addedBy?.name ?? ""}", + fontSize: 14.sp, + fontWeight: FontWeight.w400, + textAlign: TextAlign.left, + ), + ], + ).addPaddingVertical(12), + ), + // EditIcon( + // onTap: () async { + // final result = await _gotoAddEditMemoryBoxScreen(data); + // + // if (result == true) { + // controller.getMemoryList(); + // } + // }, + // ).addPaddingVertical(12), + // 12.horizontalSpace, + ], + ), + ), + ); + } + + Future _gotoAddEditMemoryBoxScreen([data, bool viewOnly = false]) async { + final res = await Navigator.pushNamed( + context, + CustomRouteNames.kAddEditMemoryBoxScreen, + arguments: AddEditMemoryBoxScreenArgs( + userData: widget.userData, + data: data, + viewOnly: viewOnly, + ), + ); + + if (res == true) { + controller.getMemoryList(); + } + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/recent_incidents_screen.dart b/lib/view/screens/clients/recent_incidents_screen.dart new file mode 100644 index 0000000..a862061 --- /dev/null +++ b/lib/view/screens/clients/recent_incidents_screen.dart @@ -0,0 +1,185 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/dialogs/app_dialogs.dart'; +import 'package:ftc_mobile_app/models/clients/recent_incidents_model.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/custom_app_bar_with_action.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/edit_icon.dart'; +import 'package:ftc_mobile_app/view/screens/clientsListing/widgets/search_bar_widget.dart'; +import 'package:get/get.dart'; +import '../../../ftc_mobile_app.dart'; +import 'add_new_recent_incident_screen.dart'; + +class RecentIncidentsScreen extends StatefulWidget { + final UserData userData; + + const RecentIncidentsScreen({Key? key, required this.userData}) + : super(key: key); + + @override + State createState() => _RecentIncidentsScreenState(); +} + +class _RecentIncidentsScreenState extends State { + late final RecentIncidentsScreenController controller; + + @override + void initState() { + controller = Get.put(RecentIncidentsScreenController(widget.userData)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + // onBackButton: () => controller.onBackPress(context), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + // sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: _appBar(context), + body: SafeArea( + child: Column( + children: [ + 8.verticalSpace, + SearchBarWidget( + controller: controller.searchTEC, + onSearchTextChange: controller.onSearch, + ), + 8.verticalSpace, + Expanded( + child: Obx( + () => controller.recentIncidentsList.isEmpty + ? FrequentFunctions.centerText(text: "No data found") + : ListView.separated( + itemCount: controller.recentIncidentsList.length, + separatorBuilder: (_, index) => 12.verticalSpace, + padding: REdgeInsets.symmetric(horizontal: 20.r), + itemBuilder: (_, int index) { + return RecentIncidentTile( + data: controller.recentIncidentsList[index], + onTap: () { + AppDialog.showRecentIncidentDetailDialog( + data: controller.recentIncidentsList[index]); + }, + onEdit: () async { + dynamic result = await Navigator.pushNamed( + context, + CustomRouteNames + .kAddNewRecentIncidentsScreenRoute, + arguments: AddNewRecentIncidentsScreenArgs( + incidentsModel: controller + .recentIncidentsList[index])); + + if (result is RecentIncidentsModel) { + controller.recentIncidentsList + .replaceRange(index, index + 1, [result]); + controller.recentIncidentsList.refresh(); + } + }, + ); + }), + ), + ), + ], + ), + ), + ); + } + + AppBar _appBar(BuildContext context) { + return CustomAppBarWithAction( + context, + titleText: "Recent Incidents", + actionText: '+ Add New', + onActionTap: () async { + dynamic result = await Navigator.pushNamed( + context, CustomRouteNames.kAddNewRecentIncidentsScreenRoute, + arguments: AddNewRecentIncidentsScreenArgs( + userId: controller.serviceUser()!.id!)); + if (result is RecentIncidentsModel) { + controller.recentIncidentsList.insert(0, result); + controller.recentIncidentsList = controller.recentIncidentsList; + } + }, + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} + +class RecentIncidentTile extends StatelessWidget { + final RecentIncidentsModel data; + final VoidCallback onTap; + final VoidCallback onEdit; + + const RecentIncidentTile({ + super.key, + required this.data, + required this.onTap, + required this.onEdit, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Container( + alignment: Alignment.centerLeft, + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.symmetric(horizontal: 13.sp, vertical: 10), + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kSecondaryColor), + borderRadius: BorderRadius.circular(10.r), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: CustomTextWidget( + text: data.incidentTitle.isNotEmpty + ? data.incidentTitle + : "Untitled Incident", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + maxLines: 1, + textAlign: TextAlign.left, + fontColor: CustomAppColors.kSecondaryColor, + ), + ), + // 8.horizontalSpace, + // EditIcon(onTap: onEdit), + ], + ), + Text.rich(TextSpan(children: [ + TextSpan( + text: "Incident Date - Time : ", + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 12.sp, + color: Colors.black, + )), + TextSpan( + text: DateFormatter.ddMMyyyyhhmmFormat( + DateTime.fromMillisecondsSinceEpoch(data.incidentDate) + .toLocal()), + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 12.sp, + color: CustomAppColors.kLightGreyColor, + )), + ])), + ], + ), + ), + ); + } +} diff --git a/lib/view/screens/clients/risk_assessments_screen.dart b/lib/view/screens/clients/risk_assessments_screen.dart new file mode 100644 index 0000000..a7b3561 --- /dev/null +++ b/lib/view/screens/clients/risk_assessments_screen.dart @@ -0,0 +1,258 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../../ftc_mobile_app.dart'; + +class RiskAssessmentsScreen extends StatefulWidget { + const RiskAssessmentsScreen({Key? key}) : super(key: key); + + @override + State createState() => _RiskAssessmentsScreenState(); +} + +class _RiskAssessmentsScreenState extends State { + RiskAssessmentsScreenController controller = Get.put(RiskAssessmentsScreenController()); + + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + // sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox(width: 15.w,), + CustomTextWidget( + text: 'Risk Assessments', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + const Spacer(), + InkWell( + onTap: () async { + // await Navigator.pushNamed( + // controller.screenKey.currentContext!, + // CustomRouteNames.kNewNoteScreenRoute, + // ); + }, + child: CustomTextWidget( + text: '+ Add New', + isExpanded: false, + fontSize: 14.sp, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kLightTextColor, + ), + ), + ], + ), + ), + + body: Container( + // padding: EdgeInsets.symmetric(horizontal: 10.w), + child: SingleChildScrollView( + child: Column( + children: [ + Container( + height: 40, + margin: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h), + decoration: BoxDecoration( + border: Border.all( + color: CustomAppColors.kLightGreyColor, + ), + borderRadius: BorderRadius.circular(5.r)), + child: Row( + children: [ + IconButton( + onPressed: () {}, icon: const Icon(Icons.search)), + CustomTextWidget( + text: "Search...", + fontSize: 18.sp, + fontWeight: FontWeight.w400, + fontColor: CustomAppColors.kLightGreyColor, + isExpanded: false), + ], + ), + ), + InkWell( + // onTap: () { + // Navigator.pushNamed( + // controller.screenKey.currentContext!, + // CustomRouteNames.kDocumentDetailsScreenRoute, + // ); + // }, + child: SizedBox( + width: MediaQuery.of(context).size.width, + child: Column( + // crossAxisAlignment: CrossAxisAlignment.center, + children: [ + raListHeading(), + raListItemWidget(text1: "Jaylon George",text2: "High",text3: "Aug/01/2024",color: true), + raListItemWidget(text1: "Jaylon George",text2: "Low",text3: "Sept/01/2024",color: false), + raListItemWidget(text1: "Jaylon George",text2: "Medium",text3: "Aug/01/2024",color: true), + raListItemWidget(text1: "Jaylon George",text2: "High",text3: "Sept/01/2024",color: false), + raListItemWidget(text1: "Jaylon George",text2: "Low",text3: "Aug/01/2024",color: true), + raListItemWidget(text1: "Jaylon George",text2: "High",text3: "Sept/01/2024",color: false), + raListItemWidget(text1: "Jaylon George",text2: "Low",text3: "Aug/01/2024",color: true), + raListItemWidget(text1: "Jaylon George",text2: "Medium",text3: "Sept/01/2024",color: false), + raListItemWidget(text1: "Jaylon George",text2: "High",text3: "Aug/01/2024",color: true), + raListItemWidget(text1: "Jaylon George",text2: "Low",text3: "Sept/01/2024",color: false), + ], + ), + ), + ) + ], + ), + ), + ), + ); + + } + + Widget raListHeading() { + return Container( + padding: EdgeInsets.symmetric(horizontal: 10.w), + decoration: const BoxDecoration( + border: Border( + top: BorderSide( + color: CustomAppColors.kSmokeColor, + ), + bottom: BorderSide(color: CustomAppColors.kSmokeColor), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 2, + child: Container( + // decoration: const BoxDecoration( + // border: Border( + // right: BorderSide( + // color: CustomAppColors.kSmokeColor, + // ), + // ), + // ), + padding: const EdgeInsets.only(right: 0, top: 10, bottom: 10), + child: CustomTextWidget( + text: "Service User", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 12.sp, + ), + ), + ), + Container( + width: 2.w, + color: CustomAppColors.kSmokeColor, + ), + Expanded( + flex: 1, + child: Container( + decoration: BoxDecoration( + border: Border( + left: BorderSide( + color: CustomAppColors.kSmokeColor, + width: 2.w + ), + ), + ), + padding: const EdgeInsets.only(right: 0, top: 10, bottom: 10), + child: CustomTextWidget( + text: "Risk", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 12.sp, + ), + ), + ), + ], + ), + ); + } + + Widget raListItemWidget({required String text1,required String text2,required String text3,required bool color, Color? centerBorderColor}) { + return InkWell( + onTap: (){ + Navigator.pushNamed(context, CustomRouteNames.kRiskAssessmentsTemplateScreenRoute); + }, + child: Container( + padding: EdgeInsets.only(right: 3.w,left: 10.w), + height: 50.h, + decoration: BoxDecoration( + color: color ? CustomAppColors.kBlueColor.withAlpha(20) : null, + border: Border( + top: BorderSide( + color: CustomAppColors.kSmokeColor, + ), + bottom: BorderSide(color: CustomAppColors.kSmokeColor), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 2, + child: Container( + padding: const EdgeInsets.only(top: 10, bottom: 10), + child: Row( + children: [ + CustomImageWidget(imagePath: AssetsManager.kManImagePng,width: 24.w,height: 24.h), + Container( + padding: const EdgeInsets.only(left: 5,), + child: CustomTextWidget( + text: text1, + textAlign: TextAlign.left, + fontSize: 11.sp, + isExpanded: false,), + ), + ], + ), + ), + ), + Container( + width: 2.w, + color: text2.toLowerCase() == "low" + ? CustomAppColors.kDarkGreenColor + : text2.toLowerCase() == "medium" + ? CustomAppColors.kYellowColor + : text2.toLowerCase() == "high" + ? CustomAppColors.kDarkRedColor + : centerBorderColor ?? CustomAppColors.kLightGreyColor, + ), + Expanded( + flex: 1, + child: Container( + padding: EdgeInsets.only(top: 10.h, bottom: 10.h,left: 12.w,right: 19.w), + child: CustomTextWidget( + text: text2, + textAlign: TextAlign.left, + fontSize: 12.sp, + isExpanded: false,), + ), + ), + ], + ), + ), + ); + } + +} diff --git a/lib/view/screens/clients/risk_assessments_template_screen.dart b/lib/view/screens/clients/risk_assessments_template_screen.dart new file mode 100644 index 0000000..e38efa7 --- /dev/null +++ b/lib/view/screens/clients/risk_assessments_template_screen.dart @@ -0,0 +1,383 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/clients/riskAssessmentResponse/RiskAssessmentData.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/custom_app_bar_with_action.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/my_circle_image.dart'; +import 'package:get/get.dart'; +import '../../../ftc_mobile_app.dart'; +import 'addEditRiskAssessment/add_edit_risk_assessment_screen.dart'; + +class RiskAssessmentsTemplateScreen extends StatefulWidget { + final UserData userData; + + const RiskAssessmentsTemplateScreen({Key? key, required this.userData}) + : super(key: key); + + @override + State createState() => + _RiskAssessmentsTemplateScreenState(); +} + +class _RiskAssessmentsTemplateScreenState + extends State { + late final RiskAssessmentsTemplateScreenController controller; + + @override + void initState() { + controller = + Get.put(RiskAssessmentsTemplateScreenController(widget.userData.id!)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + appBar: _appBar(context), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: Column( + children: [ + serviceUserDetailWidget(), + 16.verticalSpace, + Expanded(child: Obx(() { + return controller.list.isEmpty + ? FrequentFunctions.centerText(text: "No data found") + : listView(); + })), + ], + ), + ), + ); + } + + AppBar _appBar(BuildContext context) { + return CustomAppBarWithAction( + context, + titleText: 'Risk Assessments Template', + actionText: '+ Add New', + onActionTap: () async { + final result = await Navigator.pushNamed( + context, CustomRouteNames.kAddEditRiskAssessmentScreen, + arguments: AddEditRiskAssessmentScreenArgs( + userData: widget.userData, + )); + if (result == true) { + controller.getRiskAssessments(); + } + }, + ); + } + + Widget serviceUserDetailWidget() { + return Padding( + padding: REdgeInsets.symmetric(horizontal: 10), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Align( + alignment: Alignment.center, + child: MyCircleImage( + imageSize: 80.r, + url: + "${WebUrls.baseUrl}${widget.userData.profilePictureUrl ?? ""}", + errorWidget: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + height: 53.r, + width: 53.r, + ), + ), + ), + 10.verticalSpace, + CustomTextWidget( + text: widget.userData.displayName, + fontSize: 14.sp, + fontWeight: FontWeight.w600), + 32.verticalSpace, + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: _labelValueWidgets( + 'Email:', + widget.userData.email ?? "", + ), + )), + 8.horizontalSpace, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: _labelValueWidgets( + 'Contact Number:', + widget.userData.modelId?.phoneNo ?? "", + ), + )), + ], + ), + 16.verticalSpace, + ..._labelValueWidgets( + 'Address 1:', + widget.userData.modelId?.suAddress1 ?? "", + ), + 16.verticalSpace, + ..._labelValueWidgets( + 'Address 2:', + widget.userData.modelId?.suAddress2 ?? "", + ), + 16.verticalSpace, + ], + ), + ); + } + + List _labelValueWidgets(String label, String value) { + return [ + CustomTextWidget( + textAlign: TextAlign.left, + isExpanded: false, + text: label, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kLightTextColor, + fontSize: 12.sp), + 4.verticalSpace, + CustomTextWidget( + textAlign: TextAlign.left, + text: value, + isExpanded: false, + fontWeight: FontWeight.w400, + fontColor: CustomAppColors.kBlackColor, + fontSize: 13.sp), + ]; + } + + Widget _tableHeading(String text) { + return TableCell( + child: Padding( + padding: REdgeInsets.symmetric(horizontal: 12, vertical: 12), + child: Text( + text, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w600, + ), + ), + ), + ); + } + + Widget listView() { + return InteractiveViewer( + constrained: false, + scaleEnabled: false, + child: Table( + border: TableBorder.all( + color: CustomAppColors.kSmokeColor, + ), + columnWidths: { + 0: FixedColumnWidth(200.r), + 1: FixedColumnWidth(300.r), + 2: FixedColumnWidth(300.r), + 3: FixedColumnWidth(250.r), + 4: FixedColumnWidth(400.r), + 5: FixedColumnWidth(250.r), + 6: FixedColumnWidth(250.r), + }, + defaultVerticalAlignment: TableCellVerticalAlignment.middle, + children: [ + TableRow( + children: [ + _tableHeading("Hazard"), + _tableHeading("Person (s) exposed to Hazard"), + _tableHeading("Risk Identified"), + _splitColumn("Pure Risk Rating", splitCells: [ + _splitCellExpanded("C", borderRight: true), + _splitCellExpanded("L", borderRight: true), + _splitCellExpanded("R"), + ]), + _tableHeading("Control Measures Required"), + _splitColumn("In Place", splitCells: [ + _splitCellExpanded("Y", borderRight: true), + _splitCellExpanded("N"), + ]), + _splitColumn("Residual Risk Rating", splitCells: [ + _splitCellExpanded("C", borderRight: true), + _splitCellExpanded("L", borderRight: true), + _splitCellExpanded("R"), + ]), + ], + ), + ...controller.list + .map((e) => tableRow(controller.list.indexOf(e), e)) + .toList() + ], + ), + ); + } + + Widget _splitColumn(String heading, {List splitCells = const []}) { + return TableCell( + child: Column( + children: [ + Padding( + padding: REdgeInsets.all(8.0), + child: Text( + heading, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w600, + ), + ), + ), + const Divider( + height: 1, + color: CustomAppColors.kSmokeColor, + ), + Row(children: splitCells) + ], + ), + ); + } + + Expanded _splitCellExpanded(String text, {bool borderRight = false}) => + Expanded( + child: Container( + padding: REdgeInsets.all(8.0), + alignment: Alignment.center, + decoration: !borderRight + ? null + : const BoxDecoration( + border: Border( + right: BorderSide( + color: CustomAppColors.kSmokeColor, + ))), + child: Text( + text, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w600, + ), + ), + ), + ); + + TableRow tableRow(index, RiskAssessmentData e) { + return TableRow( + decoration: BoxDecoration( + color: controller.list.indexOf(e) % 2 == 0 + ? CustomAppColors.kLightGreyColor.withOpacity(0.25) + : Colors.white), + children: [ + _dataCell(e.hazard ?? ""), + _dataCell(e.personsExposedToHazard ?? ""), + _dataCell(e.riskIdentified ?? ""), + + //Pure risk rating + Row(children: [ + Expanded( + child: _dataCell( + (e.pureRiskRating?.c ?? 0).toString(), + borderRight: true, + )), + Expanded( + child: _dataCell( + (e.pureRiskRating?.l ?? 0).toString(), + borderRight: true, + )), + Expanded( + child: _dataCell( + (e.pureRiskRating?.r ?? 0).toString(), + borderLeft: true, + borderColorLeft: getRatingColor(e.pureRiskRating?.r ?? 0), + ), + ), + ]), + + _dataCell(e.coldMeasureRequired ?? ""), + + //In Place + Row(children: [ + Expanded( + child: _dataCell( + (e.inPlace?.y ?? 0).toString(), + borderRight: true, + ), + ), + Expanded(child: _dataCell((e.inPlace?.n ?? 0).toString())), + ]), + + //Residual Risk Rating + Row(children: [ + Expanded( + child: _dataCell( + (e.residualRiskRating?.c ?? 0).toString(), + borderRight: true, + ), + ), + Expanded( + child: _dataCell( + (e.residualRiskRating?.l ?? 0).toString(), + borderRight: true, + ), + ), + Expanded( + child: _dataCell( + (e.residualRiskRating?.r ?? 0).toString(), + borderLeft: true, + borderColorLeft: getRatingColor(e.residualRiskRating?.r ?? 0), + ), + ), + ]), + ], + ); + } + + //1-3 Green + // 4-6 Yellow + // 7-9 Red + Color getRatingColor(int r) { + if (r >= 0 && r <= 3) return Colors.green; + if (r >= 4 && r <= 6) return Colors.amberAccent; + if (r >= 7) return Colors.red; + return Colors.transparent; + } + + Widget _dataCell(String text, + {bool borderRight = false, + bool borderLeft = false, + Color borderColorLeft = Colors.transparent}) { + return Container( + height: 80.h, + alignment: Alignment.center, + decoration: BoxDecoration( + border: Border( + left: BorderSide( + width: borderLeft ? 3.r : 0, + color: borderColorLeft, + ), + right: BorderSide( + width: borderRight ? 1 : 0, + color: CustomAppColors.kSmokeColor, + ), + ), + ), + padding: REdgeInsets.symmetric(vertical: 8.0, horizontal: 12), + child: Text( + text, + style: TextStyle( + fontSize: 11.sp, + ), + textAlign: TextAlign.left, + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/clients/select_note_screen.dart b/lib/view/screens/clients/select_note_screen.dart new file mode 100644 index 0000000..028fa48 --- /dev/null +++ b/lib/view/screens/clients/select_note_screen.dart @@ -0,0 +1,265 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../ftc_mobile_app.dart'; + +class SelectNoteScreen extends StatefulWidget { + const SelectNoteScreen({Key? key}) : super(key: key); + + @override + State createState() => _SelectNoteScreenState(); +} + +class _SelectNoteScreenState extends State { + + SelectNoteScreenController controller = Get.put(SelectNoteScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBar( + leadingButton: Container(), + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox(width: 15.w,), + CustomTextWidget( + text: 'Select Note', + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + const Spacer(), + InkWell( + onTap: () async { + await Navigator.pushNamed( + controller.screenKey.currentContext!, + CustomRouteNames.kNewNoteScreenRoute, + ); + }, + child: CustomTextWidget( + text: '+ Add New', + isExpanded: false, + fontSize: 14.sp, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kLightTextColor, + ), + ), + ], + ), + ), + body: Center( + child: SingleChildScrollView( + child: Column( + children: [ + Container( + height: 40, + margin: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h), + decoration: BoxDecoration( + border: Border.all( + color: CustomAppColors.kLightGreyColor, + ), + borderRadius: BorderRadius.circular(40.r)), + child: Row( + children: [ + IconButton( + onPressed: () {}, icon: const Icon(Icons.search)), + CustomTextWidget( + text: "Search...", + fontSize: 18.sp, + fontWeight: FontWeight.w400, + fontColor: CustomAppColors.kLightGreyColor, + isExpanded: false), + ], + ), + ), + Container( + padding: EdgeInsets.only(left: 20.w,right: 20.w), + alignment: Alignment.topLeft, + height: 60.h, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + CustomTextWidget( + text: "Choose Categories", + fontSize: 14.sp, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kDarkBlueTextColor, + isExpanded: false), + const SizedBox(height: 10,), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + const SelectCategoryWidget(selected: true), + SizedBox(width: 5.w,), + const SelectCategoryWidget(textOfButton: "Category 2"), + SizedBox(width: 5.w,), + const SelectCategoryWidget(textOfButton: "Category 3"), + SizedBox(width: 5.w,), + const SelectCategoryWidget(textOfButton: "Category 4"), + SizedBox(width: 5.w,), + const SelectCategoryWidget(textOfButton: "Category 5"), + SizedBox(width: 5.w,), + const SelectCategoryWidget(textOfButton: "Category 6"), + SizedBox(width: 5.w,), + const SelectCategoryWidget(textOfButton: "Category 7"), + SizedBox(width: 5.w,), + const SelectCategoryWidget(textOfButton: "Category 8"), + ], + ), + ), + ], + ), + ), + Container( + padding: EdgeInsets.only(left: 25.w,top: 10), + child: CustomTextWidget( + alignment: Alignment.topLeft, + text: "Notes", + fontSize: 14.sp, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kDarkBlueTextColor, + isExpanded: false), + ), + SizedBox( + height: MediaQuery.of(context).size.height/1.45, + child: ListView.builder( + itemCount: controller.users.length, + itemBuilder: (BuildContext context, int index) { + return NotesRoundOutlinedBox2( + context: context, + child: BuildNotesList2( + history: controller.user.aboutPatient, + date: controller.user.diagnosisDate, + userName: controller.users[index], + ), + ); + }, + ), + ), + ], + ), + ), + ), + ); + } +} + +class SelectCategoryWidget extends StatelessWidget { + const SelectCategoryWidget({ + super.key,this.selected,this.textOfButton + }); + + final String? textOfButton; + final bool? selected; + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only( + left: 5.w, right: 5.w, bottom: 3.h, top: 3.h), + decoration: BoxDecoration( + color: selected!=null? CustomAppColors.kSecondaryColor:CustomAppColors.kPrimaryColor, + borderRadius: BorderRadius.circular(20.r), + border: Border.all(color: CustomAppColors.kSecondaryColor), + ), + child: CustomTextWidget( + text: textOfButton??"Category 1", + fontSize: 12.sp, + fontWeight: FontWeight.w400, + fontColor: selected!=null?CustomAppColors.kWhiteColor : CustomAppColors.kSecondaryColor, + isExpanded: false), + ); + } +} + +class NotesRoundOutlinedBox2 extends StatelessWidget { + const NotesRoundOutlinedBox2({ + super.key, + required this.context, + required this.child, + }); + + final BuildContext context; + final Widget child; + + @override + Widget build(BuildContext context) { + return Container( + + alignment: Alignment.centerLeft, + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.symmetric(horizontal: 25.w,vertical: 5.h), + padding: EdgeInsets.all(16.sp), + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kSecondaryColor), + borderRadius: BorderRadius.circular(18.r), + ), + child: child, + ); + } +} + +class BuildNotesList2 extends StatelessWidget { + const BuildNotesList2({ + super.key, + required this.date, + required this.history, required this.userName, + }); + + final String date; + final String history; + final String userName; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + CustomTextWidget( + text: 'Note Title', + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + const Spacer(), + CustomImageWidget( + imagePath: AssetsManager.kGoToArrowIcon, + height: 18.66.h, + width: 18.w, + ), + ], + ), + SizedBox(height: 5.h), + CustomTextWidget( + alignment: Alignment.centerLeft, + textAlign: TextAlign.left, + text: history, + isExpanded: false, + fontSize: 10.sp, + fontColor: CustomAppColors.kBlackColor, + ), + ], + ); + } +} \ No newline at end of file diff --git a/lib/view/screens/clientsListing/clients_list_screen.dart b/lib/view/screens/clientsListing/clients_list_screen.dart new file mode 100644 index 0000000..9a59c96 --- /dev/null +++ b/lib/view/screens/clientsListing/clients_list_screen.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; +import '../../../models/profileData/user_data.dart'; +import 'widgets/clients_list_view.dart'; +import 'widgets/search_bar_widget.dart'; + +class ClientsListScreen extends StatefulWidget { + const ClientsListScreen({Key? key}) : super(key: key); + + @override + State createState() => _ClientsListScreenState(); +} + +class _ClientsListScreenState extends State { + final controller = Get.put(ClientsListScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + body: SafeArea( + child: Column( + children: [ + SearchBarWidget( + controller: controller.searchController, + onSearchTextChange: controller.searchText, + ), + Expanded( + child: Obx(() { + final list = controller.serviceUsersList(); + final canLoadMore = controller.canLoadMore.value; + + return UsersListView( + refreshController: controller.listRC, + scrollController: controller.listSC, + canLoadMore: canLoadMore, + list: list, + onRefresh: controller.onRefresh, + onLoading: controller.onLoading, + listItemTap: (int index, UserData userData) { + Navigator.pushNamed( + context, CustomRouteNames.kClientsProfileScreenRoute, + arguments: controller.serviceUsersList[index]); + }, + ); + }), + ), + ], + ), + ), + ); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/view/screens/clientsListing/widgets/client_card_widget.dart b/lib/view/screens/clientsListing/widgets/client_card_widget.dart new file mode 100644 index 0000000..9c18e98 --- /dev/null +++ b/lib/view/screens/clientsListing/widgets/client_card_widget.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/my_circle_image.dart'; + +class UserCardWidget extends StatelessWidget { + const UserCardWidget({ + super.key, + required this.index, + required this.userData, + }); + + final UserData userData; + final int index; + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(left: 20.w, top: 10.h, bottom: 10.h), + child: Row( + children: [ + MyCircleImage( + imageSize: 53.r, + url: "${WebUrls.baseUrl}${userData.profilePictureUrl ?? ""}", + errorWidget: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + height: 53.h, + width: 53.w, + ), + ), + 10.horizontalSpace, + CustomTextWidget( + text: userData.displayName, + fontSize: 16.sp, + fontWeight: FontWeight.w600, + isExpanded: false, + ) + ], + ), + ); + } +} diff --git a/lib/view/screens/clientsListing/widgets/clients_list_view.dart b/lib/view/screens/clientsListing/widgets/clients_list_view.dart new file mode 100644 index 0000000..3465d78 --- /dev/null +++ b/lib/view/screens/clientsListing/widgets/clients_list_view.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/profileData/user_data.dart'; +import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; +import 'client_card_widget.dart'; + +class UsersListView extends StatelessWidget { + final RefreshController refreshController; + final ScrollController scrollController; + final bool canLoadMore; + final List list; + final VoidCallback onRefresh; + final VoidCallback onLoading; + final Function(int index, UserData userData) listItemTap; + + const UsersListView({ + super.key, + required this.listItemTap, + required this.refreshController, + required this.scrollController, + required this.canLoadMore, + required this.onRefresh, + required this.onLoading, + required this.list, + }); + + @override + Widget build(BuildContext context) { + return SmartRefresher( + key: const ValueKey("clients_list"), + controller: refreshController, + scrollController: scrollController, + header: FrequentFunctions.waterDropHeader, + enablePullUp: canLoadMore, + onRefresh: onRefresh, + onLoading: onLoading, + child: (list.isEmpty) + ? Container( + color: Colors.white, + child: const Center( + child: Text("No users found"), + ), + ) + : ListView.builder( + itemCount: list.length, + itemBuilder: (BuildContext context, int index) { + return InkWell( + onTap: () { + listItemTap(index, list[index]); + }, + child: UserCardWidget(index: index, userData: list[index]), + ); + }, + ), + ); + } +} diff --git a/lib/view/screens/clientsListing/widgets/search_bar_widget.dart b/lib/view/screens/clientsListing/widgets/search_bar_widget.dart new file mode 100644 index 0000000..b167a3e --- /dev/null +++ b/lib/view/screens/clientsListing/widgets/search_bar_widget.dart @@ -0,0 +1,44 @@ +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:flutter/material.dart'; + +class SearchBarWidget extends StatelessWidget { + final TextEditingController? controller; + final ValueChanged onSearchTextChange; + + const SearchBarWidget( + {super.key, required this.onSearchTextChange, this.controller}); + + @override + Widget build(BuildContext context) { + return Container( + height: 48.h, + margin: REdgeInsets.symmetric(horizontal: 16, vertical: 10), + decoration: BoxDecoration( + border: Border.all( + color: CustomAppColors.kLightGreyColor, + ), + borderRadius: BorderRadius.circular(40.r)), + child: Row( + children: [ + IconButton(onPressed: () {}, icon: const Icon(Icons.search)), + Expanded( + child: TextField( + controller: controller, + onChanged: onSearchTextChange, + autofocus: false, + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w400, + ), + decoration: const InputDecoration( + isDense: true, + border: InputBorder.none, + hintText: "Search...", + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/view/screens/export_screens.dart b/lib/view/screens/export_screens.dart new file mode 100644 index 0000000..22ef693 --- /dev/null +++ b/lib/view/screens/export_screens.dart @@ -0,0 +1,6 @@ +export 'auth_module/export_auth_module.dart'; +export 'clients/export_clients_module.dart'; +export 'home/export_home.dart'; +export 'notifications/export_notifications.dart'; +export 'profile/export_profile.dart'; +export 'rota/export_rota_screens.dart'; \ No newline at end of file diff --git a/lib/view/screens/home/dashboard_screen.dart b/lib/view/screens/home/dashboard_screen.dart new file mode 100644 index 0000000..7acc4aa --- /dev/null +++ b/lib/view/screens/home/dashboard_screen.dart @@ -0,0 +1,198 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; +import 'home_screen.dart'; + +class DashboardScreen extends StatefulWidget { + const DashboardScreen({Key? key}) : super(key: key); + + @override + State createState() => _DashboardScreenState(); +} + +class _DashboardScreenState extends State { + final controller = Get.put(DashboardScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + appBar: appBar, + sideDrawer: const CustomDrawer(), + bottomMenu: Obx( + () => BottomNavigationBar( + type: BottomNavigationBarType.fixed, + currentIndex: controller.selectedIndex.value, + onTap: (value) { + if (value == 0) { + Navigator.pushNamed( + context, CustomRouteNames.kRotaDashboardScreenRoute); + return; + } + controller.selectedIndex.value = value; + }, + items: [ + BottomNavigationBarItem( + label: "", + icon: Column( + children: [ + CustomImageWidget( + imagePath: AssetsManager.kCalendarIcon, + height: 26.h, + width: 26.w, + imageColor: controller.selectedIndex.value == 0 + ? CustomAppColors.kSecondaryColor + : null, + ), + CustomTextWidget( + text: "Rota", + isExpanded: false, + fontSize: 12.sp, + fontColor: controller.selectedIndex.value == 0 + ? CustomAppColors.kSecondaryColor + : null), + ], + ), + ), + BottomNavigationBarItem( + label: "", + icon: Column( + children: [ + CustomImageWidget( + imagePath: AssetsManager.kHomeIcon, + height: 26.h, + width: 26.w, + imageColor: controller.selectedIndex.value == 1 + ? CustomAppColors.kSecondaryColor + : null, + ), + CustomTextWidget( + text: "Dashboard", + isExpanded: false, + fontSize: 12.sp, + fontColor: controller.selectedIndex.value == 1 + ? CustomAppColors.kSecondaryColor + : null, + ), + ], + ), + ), + BottomNavigationBarItem( + label: "", + icon: Column( + children: [ + CustomImageWidget( + imagePath: AssetsManager.kMessageIcon, + height: 26.h, + width: 26.w, + imageColor: controller.selectedIndex.value == 2 + ? CustomAppColors.kSecondaryColor + : null, + ), + CustomTextWidget( + text: "Inbox", + isExpanded: false, + fontSize: 12.sp, + fontColor: controller.selectedIndex.value == 2 + ? CustomAppColors.kSecondaryColor + : null, + ), + ], + ), + ), + BottomNavigationBarItem( + label: "", + icon: Column( + children: [ + CustomImageWidget( + imagePath: AssetsManager.kPeopleUnselectedIcon, + height: 26.h, + width: 26.w, + imageColor: controller.selectedIndex.value == 3 + ? CustomAppColors.kSecondaryColor + : null, + ), + CustomTextWidget( + text: "Clients", + isExpanded: false, + fontSize: 12.sp, + fontColor: controller.selectedIndex.value == 3 + ? CustomAppColors.kSecondaryColor + : null, + ), + ], + ), + ), + ], + ), + ), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + body: SafeArea( + child: Obx(() => selectedScreen()[controller.selectedIndex.value])), + ); + } + + PreferredSizeWidget get appBar => CustomAppBar( + showBoxShadow: false, + titleWidget: Row( + children: [ + InkWell( + onTap: () => controller.screenKey.currentState!.openDrawer(), + child: CustomImageWidget( + imagePath: AssetsManager.kDrawerIcon, + height: 27.h, + width: 27.w, + ), + ), + Padding( + padding: EdgeInsets.only(left: 15.0.w), + child: Obx(() { + return CustomTextWidget( + text: controller.selectedIndex.value == 0 + ? 'Home' + : controller.selectedIndex.value == 1 + ? 'Home' + : controller.selectedIndex.value == 2 + ? 'Inbox' + : controller.selectedIndex.value == 3 + ? 'Clients' + : "", + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700, + fontColor: CustomAppColors.kDarkBlueTextColor, + ); + }), + ), + const Spacer(), + Obx(() { + return Visibility( + visible: controller.selectedIndex.value == 0, + child: CustomImageWidget( + imagePath: AssetsManager.kBellIcon, + height: 23.h, + width: 22.w, + ), + ); + }), + ], + ), + ); + + List selectedScreen() { + return [ + const RotaDashboardScreen(), + const HomeScreen(), + const InboxScreen(), + const ClientsListScreen(), + ]; + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/home/export_home.dart b/lib/view/screens/home/export_home.dart new file mode 100644 index 0000000..6f51457 --- /dev/null +++ b/lib/view/screens/home/export_home.dart @@ -0,0 +1,3 @@ +export 'dashboard_screen.dart'; +export '../chat/chat_screen.dart'; +export 'inbox_screen.dart'; \ No newline at end of file diff --git a/lib/view/screens/home/home_screen.dart b/lib/view/screens/home/home_screen.dart new file mode 100644 index 0000000..ad22278 --- /dev/null +++ b/lib/view/screens/home/home_screen.dart @@ -0,0 +1,214 @@ +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/my_circle_image.dart'; +import 'package:ftc_mobile_app/view/screens/rota/new_rota_list_widget.dart'; +import 'package:ftc_mobile_app/view/screens/webview/webview_screen.dart'; +import 'package:get/get.dart'; + +import 'widgets/home_row_items.dart'; +import 'widgets/line_row_widget.dart'; + +class HomeScreen extends StatelessWidget { + const HomeScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final controller = Get.find(); + return Stack( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 60.verticalSpace, + Container( + height: 400.h, + padding: EdgeInsets.only(top: 100.h), + decoration: BoxDecoration( + color: CustomAppColors.kSecondaryColor, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.r), + topRight: Radius.circular(20.r), + ), + ), + ), + ], + ), + SizedBox( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + 16.verticalSpace, + + //profile picture + Obx(() { + return MyCircleImage( + imageSize: 80.r, + url: + "${WebUrls.baseUrl}${DashboardScreenController.instance.myProfileData()?.user?.profilePictureUrl ?? ""}", + errorWidget: CircleAvatar( + backgroundColor: Colors.white, + child: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + imageColor: CustomAppColors.kDarkBlueTextColor, + height: 80.r, + width: 80.r, + ), + ), + ); + }), + 3.verticalSpace, + + //Name + Obx( + () => CustomTextWidget( + text: DashboardScreenController.instance + .myProfileData() + ?.staffMemberName ?? + "", + fontSize: 14.sp, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kPrimaryColor, + ), + ), + + //designation + Obx( + () => CustomTextWidget( + text: DashboardScreenController.instance + .myProfileData() + ?.staffDesignation ?? + "", + fontSize: 14.sp, + fontColor: CustomAppColors.kPrimaryColor, + fontWeight: FontWeight.w600, + ), + ), + 16.verticalSpace, + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + HomeRowItems( + iconUrl: AssetsManager.kCalendarIcon, + textOfItem: "Rota", + onTap: () { + Navigator.pushNamed( + context, + CustomRouteNames.kRotaDashboardScreenRoute, + ); + }, + ), + HomeRowItems( + iconUrl: AssetsManager.kPeopleUnselectedIcon, + textOfItem: "Clients", + color: CustomAppColors.kLightTextColor, + onTap: () { + Get.find() + .selectedIndex + .value = 3; + }, + ), + HomeRowItems( + iconUrl: AssetsManager.kMessageIcon, + textOfItem: "Message", + onTap: () { + Get.find() + .selectedIndex + .value = 2; + }, + ), + ], + ), + 10.verticalSpace, + Container( + height: MediaQuery.of(context).size.height / 1.5, + decoration: BoxDecoration( + color: CustomAppColors.kWhiteColor, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.r), + topRight: Radius.circular(20.r), + ), + ), + child: Column( + children: [ + Obx(() { + return (controller.myShiftsList.isEmpty) + ? FrequentFunctions.noWidget + : Padding( + padding: EdgeInsets.symmetric( + horizontal: 20.r, vertical: 10.r), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + CustomTextWidget( + text: "Your next Shifts", + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 16.sp), + InkWell( + onTap: () { + Navigator.pushNamed( + context, + CustomRouteNames + .kYourRotaScreenRoute); + }, + child: CustomTextWidget( + text: "See all", + isExpanded: false, + fontSize: 12.sp, + fontWeight: FontWeight.w600), + ), + ], + ), + ); + }), + Obx( + () => (controller.myShiftsList.isEmpty) + ? FrequentFunctions.noWidget + : NewRotaListWidget( + shifts: controller.myShiftsList, + physics: const NeverScrollableScrollPhysics(), + ), + ), + 10.verticalSpace, + InkWell( + onTap: () { + Navigator.pushNamed( + context, + CustomRouteNames.kWebviewScreen, + arguments: WebviewScreenArgument( + title: 'Policies and Procedures', + url: ConstantText.privacyUrl), + ); + }, + child: const LineRowWidget( + text: "Policies & Procedures", + icon: AssetsManager.kPoliciesIcon), + ), + InkWell( + onTap: () { + //Todo: uncomment when start working + // Navigator.pushNamed( + // context, + // CustomRouteNames.kSettingsScreen, + // ); + }, + child: const LineRowWidget( + text: "Settings", + icon: AssetsManager.kSettingsIcon), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/lib/view/screens/home/inbox_screen.dart b/lib/view/screens/home/inbox_screen.dart new file mode 100644 index 0000000..2bc008b --- /dev/null +++ b/lib/view/screens/home/inbox_screen.dart @@ -0,0 +1,202 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/chat/ChatModel.dart'; +import 'package:ftc_mobile_app/view/screens/chat/arguments/chat_screen_args.dart'; +import 'package:ftc_mobile_app/web_services/chat_services.dart'; +import 'package:get/get.dart'; +import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; +import '../../custom_widgets/my_circle_image.dart'; + +class InboxScreen extends StatefulWidget { + const InboxScreen({Key? key}) : super(key: key); + + @override + State createState() => _InboxScreenState(); +} + +class _InboxScreenState extends State { + final InboxScreenController controller = Get.put(InboxScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + // onBackButton: controller.onBackButtonPressed, + onScreenTap: controller.removeFocus, + showAppBar: false, + body: Obx(() { + final list = controller.chatsAndGroups(); + list.sort((a, b) => b.date.compareTo(a.date)); + + final canLoadMore = controller.canLoadMore.value; + + return SmartRefresher( + key: const ValueKey("clients_list"), + controller: controller.listRC, + scrollController: controller.listSC, + header: FrequentFunctions.waterDropHeader, + enablePullUp: canLoadMore, + onRefresh: controller.onRefresh, + onLoading: controller.onLoading, + child: (list.isEmpty) + ? Container( + color: Colors.white, + child: const Center( + child: Text("Your inbox is empty"), + ), + ) + : ListView.separated( + shrinkWrap: true, + itemCount: list.length, + padding: REdgeInsets.symmetric(horizontal: 20), + separatorBuilder: (_, int index) => 8.verticalSpace, + itemBuilder: (BuildContext context, int index) { + return SenderListWidget( + messagesListModel: controller.chatsAndGroups[index], + onTap: (messagesListModel) async { + await Navigator.pushNamed( + context, + CustomRouteNames.kChatScreenRoute, + arguments: ChatScreenArgs( + name: messagesListModel.title, + profilePicPath: messagesListModel.image, + otherUserId: messagesListModel.otherUserId, + groupData: messagesListModel.groupData, + onLastMessageUpdate: (m) => + _onLastMessageUpdate(index, m), + ), + ); + }, + ); + }, + ), + ); + }), + floatingActionButton: FloatingActionButton( + onPressed: () { + Navigator.pushNamed( + context, CustomRouteNames.kSelectUserForChatScreenRoute); + }, + child: const Icon(Icons.add), + ), + ); + } + + _onLastMessageUpdate(int index, ChatModel model) { + if (mounted) { + controller.chatsAndGroups[index].previewOfLastMessage = + model.message ?? ""; + controller.chatsAndGroups[index].date = + model.date ?? DateTime.now().millisecondsSinceEpoch; + + controller.chatsAndGroups.refresh(); + } + } +} + +class SenderListWidget extends StatefulWidget { + const SenderListWidget({ + super.key, + required this.messagesListModel, + required this.onTap, + }); + + final MessagesListModel messagesListModel; + final ValueChanged onTap; + + @override + State createState() => _SenderListWidgetState(); +} + +class _SenderListWidgetState extends State { + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () => widget.onTap(widget.messagesListModel), + child: Container( + padding: REdgeInsets.symmetric(vertical: 9), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyCircleImage( + imageSize: 53.r, + url: "${WebUrls.baseUrl}${widget.messagesListModel.image}", + errorWidget: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + imageColor: CustomAppColors.kDarkBlueTextColor, + height: 53.r, + width: 53.r, + ), + ), + 12.horizontalSpace, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + CustomTextWidget( + text: widget.messagesListModel.title, + fontColor: CustomAppColors.kDarkBlueTextColor, + fontSize: 16.sp, + fontWeight: FontWeight.w600, + isExpanded: false, + ), + 4.verticalSpace, + (widget.messagesListModel.messageType == + MessageType.message.name) + ? CustomTextWidget( + text: widget.messagesListModel.previewOfLastMessage, + fontColor: widget.messagesListModel.isRecent == true + ? CustomAppColors.kDarkBlueTextColor + : CustomAppColors.kLightGreyColor, + fontSize: 12.sp, + maxLines: 1, + fontWeight: FontWeight.w600, + textAlign: TextAlign.left, + isExpanded: false, + ) + : FrequentFunctions.noWidget, + ], + ), + ), + Container( + padding: REdgeInsets.only(right: 2), + child: Column( + children: [ + CustomTextWidget( + text: FrequentFunctions.toTimesAgo( + DateTime.fromMillisecondsSinceEpoch( + widget.messagesListModel.date) + .toIso8601String()), + fontColor: CustomAppColors.kLightGreyColor, + fontSize: 12.sp, + fontWeight: FontWeight.w600, + isExpanded: false, + ), + widget.messagesListModel.noOfMessages != 0 + ? Padding( + padding: EdgeInsets.only(top: 8.0.h), + child: CircleAvatar( + minRadius: 8.sp, + maxRadius: 8.sp, + backgroundColor: CustomAppColors.kBlackColor, + child: CustomTextWidget( + text: "${widget.messagesListModel.noOfMessages}", + isExpanded: false, + fontSize: 8.sp, + fontColor: CustomAppColors.kPrimaryColor, + ), + ), + ) + : Container(), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/view/screens/home/select_user_for_chat_screen.dart b/lib/view/screens/home/select_user_for_chat_screen.dart new file mode 100644 index 0000000..9023a15 --- /dev/null +++ b/lib/view/screens/home/select_user_for_chat_screen.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/controllers/home/select_user_for_chat_screen_controller.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/view/screens/chat/arguments/chat_screen_args.dart'; +import 'package:get/get.dart'; +import '../../../models/profileData/user_data.dart'; +import '../clientsListing/widgets/clients_list_view.dart'; +import '../clientsListing/widgets/search_bar_widget.dart'; + +class SelectUserForChatScreen extends StatefulWidget { + const SelectUserForChatScreen({Key? key}) : super(key: key); + + @override + State createState() => + _SelectUserForChatScreenState(); +} + +class _SelectUserForChatScreenState extends State { + final controller = Get.put(SelectUserForChatScreenController()); + + // final controller = + // Get.put(ClientsListScreenController(), tag: "select_clients"); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + // onBackButton: () => controller.backButtonPressed(context), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + appBar: CustomAppBarTitleOnly(context, titleText: 'Select Contact'), + onScreenTap: controller.removeFocus, + showAppBar: true, + body: SafeArea( + child: Column( + children: [ + SearchBarWidget( + onSearchTextChange: controller.onSearch, + ), + Expanded( + child: Obx(() { + final list = controller.staffUsersList(); + final canLoadMore = controller.canLoadMore.value; + + return UsersListView( + refreshController: controller.listRC, + scrollController: controller.listSC, + canLoadMore: canLoadMore, + list: list, + onRefresh: controller.onRefresh, + onLoading: controller.onLoading, + listItemTap: (int index, UserData userData) { + Navigator.popAndPushNamed( + controller.screenKey.currentContext!, + CustomRouteNames.kChatScreenRoute, + arguments: ChatScreenArgs( + otherUserId: userData.id!, + name: userData.displayName, + profilePicPath: userData.profilePictureUrl ?? "", + ), + ); + }, + ); + }), + ), + ], + ), + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} + +// class UserListWidgetItem extends StatelessWidget { +// final SelectUserForChatScreenController controller = +// Get.find(); +// +// UserListWidgetItem({ +// super.key, +// required this.nameOfPerson, +// required this.index, +// }); +// +// final String nameOfPerson; +// final int index; +// +// @override +// Widget build(BuildContext context) { +// return InkWell( +// onTap: () { +// // Navigator.pop(controller.screenKey.currentContext!, controller.serviceUsersList[index]); +// Navigator.pushNamed(controller.screenKey.currentContext!, +// CustomRouteNames.kGroupChatScreenRoute, +// arguments: controller.serviceUsersList[index]); +// }, +// child: Container( +// padding: EdgeInsets.only(left: 20.w, top: 20.h), +// child: Row( +// children: [ +// CustomImageWidget( +// imagePath: AssetsManager.kPersonMainIcon, +// height: 53.h, +// width: 53.w, +// ), +// SizedBox( +// width: 10.w, +// ), +// CustomTextWidget( +// text: nameOfPerson, +// fontSize: 16.sp, +// fontWeight: FontWeight.w600, +// isExpanded: false, +// ) +// ], +// ), +// ), +// ); +// } +// } diff --git a/lib/view/screens/home/widgets/home_row_items.dart b/lib/view/screens/home/widgets/home_row_items.dart new file mode 100644 index 0000000..7d8853b --- /dev/null +++ b/lib/view/screens/home/widgets/home_row_items.dart @@ -0,0 +1,52 @@ +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:flutter/material.dart'; + +class HomeRowItems extends StatelessWidget { + const HomeRowItems({ + super.key, + required this.iconUrl, + required this.textOfItem, + this.color, + this.onTap, + }); + + final String iconUrl; + final String textOfItem; + final Color? color; + final VoidCallback? onTap; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Container( + padding: EdgeInsets.symmetric(vertical: 10.w, horizontal: 10.h), + height: 80.h, + width: 80.h, + decoration: BoxDecoration( + color: CustomAppColors.kPrimaryColor, + borderRadius: BorderRadius.circular(15.r), + ), + child: Column( + children: [ + CustomImageWidget( + width: 33.w, + height: 38.h, + imagePath: iconUrl, + imageColor: CustomAppColors.kBlackColor, + ), + SizedBox( + height: 5.h, + ), + CustomTextWidget( + text: textOfItem, + isExpanded: false, + fontSize: 12.sp, + fontWeight: FontWeight.w600, + ) + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/view/screens/home/widgets/line_row_widget.dart b/lib/view/screens/home/widgets/line_row_widget.dart new file mode 100644 index 0000000..d57b99e --- /dev/null +++ b/lib/view/screens/home/widgets/line_row_widget.dart @@ -0,0 +1,42 @@ +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:flutter/material.dart'; + +class LineRowWidget extends StatelessWidget { + const LineRowWidget({super.key, required this.text, required this.icon}); + + final String text; + final String icon; + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.symmetric(horizontal: 20.w, vertical: 5.h), + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.h), + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kLightGreyColor)), + child: Row( + children: [ + CustomImageWidget( + imagePath: icon, + width: 19, + height: 22, + ), + SizedBox( + width: 25.w, + ), + CustomTextWidget( + text: text, + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp), + const Spacer(), + CustomTextWidget( + text: ">", + isExpanded: false, + fontSize: 15.sp, + fontWeight: FontWeight.w500), + ], + ), + ); + } +} diff --git a/lib/view/screens/notifications/export_notifications.dart b/lib/view/screens/notifications/export_notifications.dart new file mode 100644 index 0000000..f399065 --- /dev/null +++ b/lib/view/screens/notifications/export_notifications.dart @@ -0,0 +1 @@ +export 'notifications_list_screen.dart'; \ No newline at end of file diff --git a/lib/view/screens/notifications/notifications_list_screen.dart b/lib/view/screens/notifications/notifications_list_screen.dart new file mode 100644 index 0000000..8af17f1 --- /dev/null +++ b/lib/view/screens/notifications/notifications_list_screen.dart @@ -0,0 +1,239 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_app_badger/flutter_app_badger.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; + +class NotificationListScreen extends StatefulWidget { + const NotificationListScreen({Key? key}) : super(key: key); + + @override + State createState() => _NotificationListScreenState(); +} + +class _NotificationListScreenState extends State { + final controller = Get.put(NotificationListScreenController()); + + @override + void initState() { + try { + FlutterAppBadger.removeBadge(); + } catch (e) { + debugPrint(e.toString()); + } + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + // sideDrawer: const CustomDrawer(), + showAppBar: true, + appBar: CustomAppBarTitleOnly(context, titleText: 'Notifications'), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 25.w), + child: SingleChildScrollView( + child: Column( + children: [ + const DateTextWidget(), + NotificationListItemWidget(totalNotifications: "4"), + SizedBox( + height: 4.h, + ), + NotificationListItemWidget(), + SizedBox( + height: 4.h, + ), + const DateTextWidget(), + NotificationListItemWidget(), + const DateTextWidget(), + NotificationListItemWidget(), + SizedBox( + height: 4.h, + ), + NotificationListItemWidget(totalNotifications: "8"), + SizedBox( + height: 4.h, + ), + const DateTextWidget(), + NotificationListItemWidget(), + const DateTextWidget(), + NotificationListItemWidget(), + SizedBox( + height: 4.h, + ), + NotificationListItemWidget(), + SizedBox( + height: 4.h, + ), + const DateTextWidget(), + NotificationListItemWidget(), + SizedBox(height: 8.h), + ], + ), + ), + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} + +class DateTextWidget extends StatelessWidget { + const DateTextWidget({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric(vertical: 5.h), + child: CustomTextWidget( + alignment: Alignment.centerLeft, + textAlign: TextAlign.left, + text: "21 Dec, 2022", + isExpanded: false, + fontSize: 10.sp, + fontWeight: FontWeight.w500, + fontColor: CustomAppColors.kLightTextColor, + ), + ); + } +} + +class NotificationListItemWidget extends StatelessWidget { + NotificationListItemWidget({ + super.key, + this.totalNotifications = '0', + }); + + final NotificationListScreenController controller = Get.find(); + final String totalNotifications; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + final rotaShift = RotaShift( + name: 'Dr. John Doe', + staffRequired: '2', + workerType: 'Nurse', + location: 'Hospital A', + startTime: '8:00 AM', + endTime: '4:00 PM', + breakTime: '30 min', + notes: 'NA', + ); + showDialog( + context: context, + builder: (BuildContext context) { + return ShowDialogNotification( + rotaShift: rotaShift, + ); + }, + ); + }, + onDoubleTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return const HolidayRequestAcceptDialog( + startDate: "Dec 18, 2022", + endDate: "Dec 19, 2022", + noOfDays: "2 days (16 hours)", + ); + }, + ); + }, + onLongPress: () { + showDialog( + context: context, + builder: (BuildContext context) { + return PrivacyPolicyDialog( + privacyPolicy: controller.privacyPolicy, + checkBoxOnChange: (value) { + controller.privacyPolicyAccepted.value = value; + }, + ); + }, + ); + }, + child: Container( + alignment: Alignment.centerLeft, + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 6.h), + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kSecondaryColor), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + CustomTextWidget( + text: 'New Shift: Dec 21, 7AM - 7PM', + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + const Spacer(), + CustomTextWidget( + text: '07:30 PM', + isExpanded: false, + fontWeight: FontWeight.w500, + fontSize: 10.sp, + fontColor: CustomAppColors.kLightTextColor, + ), + ], + ), + SizedBox(height: 5.h), + Row( + children: [ + CustomTextWidget( + alignment: Alignment.centerLeft, + textAlign: TextAlign.left, + text: "You have been assigned a new shift.", + isExpanded: false, + fontSize: 10.sp, + fontWeight: FontWeight.w500, + fontColor: CustomAppColors.kLightTextColor, + ), + const Spacer(), + totalNotifications != '0' + ? ClipRRect( + borderRadius: BorderRadius.circular(80.r), + child: Container( + alignment: Alignment.center, + height: 22.h, + width: 22.w, + decoration: const BoxDecoration( + color: CustomAppColors.kRedColor, + ), + child: CustomTextWidget( + alignment: Alignment.center, + textAlign: TextAlign.left, + text: totalNotifications, + isExpanded: false, + fontSize: 12.sp, + fontWeight: FontWeight.w500, + fontColor: CustomAppColors.kWhiteColor, + ), + ), + ) + : Container(), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/view/screens/profile/export_profile.dart b/lib/view/screens/profile/export_profile.dart new file mode 100644 index 0000000..2a63f07 --- /dev/null +++ b/lib/view/screens/profile/export_profile.dart @@ -0,0 +1 @@ +export 'view_profile_screen.dart'; \ No newline at end of file diff --git a/lib/view/screens/profile/view_profile_screen.dart b/lib/view/screens/profile/view_profile_screen.dart new file mode 100644 index 0000000..5113b49 --- /dev/null +++ b/lib/view/screens/profile/view_profile_screen.dart @@ -0,0 +1,494 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/dialogs/app_dialogs.dart'; +import 'package:ftc_mobile_app/dialogs/widgets/holidays_data_dialog.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/view/custom_widgets/my_circle_image.dart'; +import 'package:get/get.dart'; +import '../../../models/staffWorkload/StaffWorkloadResponse.dart'; + +class ViewProfileScreen extends StatefulWidget { + const ViewProfileScreen({Key? key}) : super(key: key); + + @override + State createState() => _ViewProfileScreenState(); +} + +class _ViewProfileScreenState extends State { + final controller = Get.put(ViewProfileScreenController()); + + @override + Widget build(BuildContext context) { + return ObxValue((RxBool isEditable) { + final editable = isEditable(); + + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: + // isEditable() ? + CustomAppBarTitleOnly(context, titleText: "Your Profile"), + // : CustomAppBarWithAction( + // context, + // titleText: 'Your Profile', + // actionText: '\u270E Edit', + // actionTextColor: Get.theme.primaryColor, + // onActionTap: () { + // isEditable.value = true; + // }, + // ), + body: SingleChildScrollView( + child: Obx(() { + if (controller.detail()?.user == null) { + return FrequentFunctions.noWidget; + } + + final detail = controller.detail()!; + + return Padding( + padding: REdgeInsets.symmetric(horizontal: 20), + child: Column( + children: [ + Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + MyCircleImage( + imageSize: 80.r, + url: + "${WebUrls.baseUrl}${detail.user!.profilePictureUrl ?? ""}", + errorWidget: CustomImageWidget( + imagePath: AssetsManager.kPersonMainIcon, + imageColor: CustomAppColors.kDarkBlueTextColor, + height: 80.r, + width: 80.r, + ), + ), + 4.verticalSpace, + CustomTextWidget( + text: detail.user!.displayName, + fontWeight: FontWeight.w500, + fontColor: Colors.black, + ) + ], + ), + ), + // Container( + // padding: EdgeInsets.only(left: 18.w, top: 15.h), + // child: Row( + // children: [ + // // Obx( + // // () => TabBarWidget( + // // title: "Profile (Client View)", + // // selected: controller.viewProfileClient.value, + // // onTapFunction: () { + // // controller.viewProfileClient.value = true; + // // }, + // // ), + // // ), + // // SizedBox( + // // width: 12.w, + // // ), + // // Expanded( + // // child: SizedBox( + // // width: , + // // child: Obx( + // // () => TabBarWidget( + // // title: "Staff Profile", + // // selected: controller.viewProfileClient.isFalse, + // // onTapFunction: () => + // // controller.viewProfileClient.value = false, + // // ), + // // ), + // // ), + // // ), + // + // Expanded( + // child: SizedBox( + // width: 12.w, + // ), + // ), + // InkWell( + // onTap: () {}, + // borderRadius: 20.toRadius(), + // child: Padding( + // padding: REdgeInsets.symmetric(horizontal: 12, vertical: 6), + // child: Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // CustomImageWidget( + // imagePath: AssetsManager.kPencilIcon, + // width: 10.w, + // height: 10.h, + // ), + // 4.horizontalSpace, + // CustomTextWidget( + // text: "Edit", + // isExpanded: false, + // fontSize: 10.sp, + // fontWeight: FontWeight.w700, + // fontColor: CustomAppColors.kSecondaryColor), + // ], + // ), + // ), + // ), + // 12.horizontalSpace, + // ], + // ), + // ), + 16.verticalSpace, + Padding( + padding: EdgeInsets.symmetric( + vertical: 6.r, + ), + child: CustomTextFieldWidget( + controller: controller.nameTEC, + heading: "Name", + isEnabled: editable, + ), + // child: isEditable() + // ? CustomTextFieldWidget( + // controller: controller.nameTEC, + // heading: "Name", + // ) + // : SingleItem( + // heading: "Name", value: detail.user!.displayName), + ), + 16.verticalSpace, + Obx( + () => controller.viewProfileClient.isTrue + ? profileClientView + : Column( + children: [ + Row( + children: [ + Expanded( + child: CustomTextFieldWidget( + controller: controller.emailTEC, + heading: "Email", + isEnabled: false, + ), + // child: SingleItem( + // heading: "Email", + // value: detail.user!.email ?? ""), + ), + 8.horizontalSpace, + Expanded( + child: CustomTextFieldWidget( + controller: controller.phoneTEC, + heading: "Phone", + isEnabled: editable, + ), + // child: SingleItem( + // heading: "Phone", + // value: + // (detail.user!.phoneNumber ?? "")), + ), + ], + ), + 16.verticalSpace, + // SingleItem( + // heading: "Address", + // value: + // detail.user!.modelId?.homeAddress ?? ""), + CustomTextFieldWidget( + controller: controller.addressTEC, + heading: "Address", + isEnabled: false, + ), + 16.verticalSpace, + CustomTextFieldWidget( + controller: TextEditingController( + text: detail.kin ?? ""), + heading: "Next of kin", + isEnabled: false, + ), + 16.verticalSpace, + // SingleItem( + // heading: "Next of kin", + // value: detail.kin ?? ""), + SingleItem( + heading: "Total Holidays Left", + value: + "${detail.staffWorkLoads?.firstOrNull?.holidayAlwnNoOfDys ?? "0"} Days (${detail.staffWorkLoads?.firstOrNull?.holidayAlwnNoOfHours ?? "0"} hours)", + onTap: () { + if (detail.staffWorkLoads?.firstOrNull != + null) {} + _onTotalHolidaysBoxTap( + detail.staffWorkLoads!.first); + }, + ), + SingleItem( + heading: "", + value: "Training", + onTap: () { + Get.toNamed( + CustomRouteNames.kTrainingsScreen); + }), + // SingleItem( + // heading: "", + // value: "COVID Checks", + // onTap: () {}), + 12.verticalSpace, + Obx(() { + return Align( + alignment: Alignment.centerLeft, + child: IgnorePointer( + ignoring: true, + child: CustomCheckBox( + checkBoxValue: controller.covidCheck(), + titleText: "COVID Check", + onTap: () { + // controller.covidCheck.toggle(); + }, + ), + ), + ); + }), + ], + ), + ), + 20.verticalSpace, + editable + ? AppDialog.buttonsBar( + onButton1Tap: () { + isEditable(false); + }, + onButton2Tap: () {}, + button1Text: "Cancel", + button2Text: "Update") + .paddingOnly(bottom: 12.r) + : FrequentFunctions.noWidget, + + CustomAppButton( + buttonText: "LOGOUT", + buttonColor: CustomAppColors.kRedColor, + borderColor: CustomAppColors.kRedColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: () { + controller.logoutPressed(); + }, + ), + + 18.verticalSpace, + ], + ), + ); + }), + ), + ); + }, controller.isEditable); + } + + Column get profileClientView { + return const Column( + children: [ + BuildDetailsWidget( + title: "My Experience", + details: + "A quick preview of the text will be shown here. A quick preview of the text will be shown here. shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here.", + ), + BuildDetailsWidget( + title: "About Me", + details: + "A quick preview of the text will be shown here. A quick preview of the text will be shown here. shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here.", + ), + BuildDetailsWidget( + title: "Things I like to do in my spare time", + details: + "A quick preview of the text will be shown here. A quick preview of the text will be shown here. shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here. A quick preview of the text will be shown here.", + ), + ], + ); + } + + _onTotalHolidaysBoxTap(StaffWorkLoads? workLoads) { + DateTime date = DateTime.now(); + if (workLoads?.endDate.isNotNullOrEmpty() == true) { + date = DateTime.parse(workLoads!.endDate!); + } + + final holidayData = HolidayModel( + carriedOver: "${workLoads?.carriedOverHours ?? 0} hours", + holidayEntitlement: + "${workLoads?.holidayEntitlement?.numberOfWeeks ?? 0} weeks (${workLoads?.holidayEntitlement?.numberOfDays ?? 0} days, ${workLoads?.holidayEntitlement?.numberOfHours ?? 0} hours)", + holidayAllowance: + "${workLoads?.holidayAlwnNoOfDys ?? 0} days (${workLoads?.holidayAlwnNoOfHours ?? 0} hours)", + remainingHolidays: + "${workLoads?.holidaysRemaining ?? 0} days (${workLoads?.holidayAlwnNoOfHours ?? 0} hours)", + timeLeftBeforeYearEnd: "${date.difference(DateTime.now()).inDays} days", + ); + + showDialog( + context: context, + builder: (BuildContext context) { + return HolidaysDataDialog(holidayModel: holidayData); + }); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} + +class SingleItem extends StatelessWidget { + const SingleItem({ + super.key, + required this.heading, + required this.value, + this.onTap, + }); + + final String heading; + final String value; + final VoidCallback? onTap; + + @override + Widget build(BuildContext context) { + return Padding( + padding: REdgeInsets.symmetric(vertical: 10.0), + child: InkWell( + onTap: onTap, + child: Container( + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kSmokeColor)), + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + child: Row( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (heading.isEmpty) + ? FrequentFunctions.noWidget + : CustomTextWidget( + alignment: Alignment.topLeft, + textAlign: TextAlign.left, + text: heading, + fontWeight: FontWeight.w500, + isExpanded: false, + fontSize: 10.sp, + fontColor: CustomAppColors.kLightTextColor, + ).paddingOnly(bottom: 4), + CustomTextWidget( + alignment: Alignment.topLeft, + textAlign: TextAlign.left, + text: value, + fontWeight: FontWeight.w600, + isExpanded: false, + fontSize: 13.sp, + fontColor: CustomAppColors.kDarkBlueTextColor, + ), + ], + ), + onTap != null ? const Spacer() : Container(), + onTap != null + ? Transform.rotate( + angle: 3.1415 * 3.5, + child: const Icon( + Icons.arrow_drop_down, + ), + ) + : Container(), + ], + ), + ), + ), + ); + } +} + +class TabBarWidget extends StatelessWidget { + const TabBarWidget({ + super.key, + required this.selected, + required this.title, + this.onTapFunction, + }); + + final bool selected; + final String title; + final VoidCallback? onTapFunction; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTapFunction, + child: Container( + // width: 141.w, + height: 22.h, + padding: EdgeInsets.symmetric(horizontal: 12.w), + decoration: BoxDecoration( + color: selected + ? CustomAppColors.kSecondaryColor + : CustomAppColors.kPrimaryColor, + borderRadius: BorderRadius.circular(20.r), + border: Border.all( + color: selected + ? CustomAppColors.kSecondaryColor + : CustomAppColors.kSmokeColor), + ), + child: CustomTextWidget( + text: title, + isExpanded: false, + fontColor: selected + ? CustomAppColors.kPrimaryColor + : CustomAppColors.kBlackColor, + fontSize: 14.sp, + fontWeight: FontWeight.w500, + ), + ), + ); + } +} + +class BuildDetailsWidget extends StatelessWidget { + const BuildDetailsWidget({ + super.key, + required this.details, + required this.title, + }); + + final String title; + final String details; + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 20.w), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: EdgeInsets.only(bottom: 8.h), + child: CustomTextWidget( + isExpanded: false, + text: title, + fontWeight: FontWeight.w600, + fontColor: CustomAppColors.kDarkBlueTextColor, + fontSize: 14.sp), + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 12.h), + decoration: BoxDecoration( + border: Border.all(color: CustomAppColors.kLightTextColor), + borderRadius: BorderRadius.circular(10.r)), + child: CustomTextWidget( + alignment: Alignment.centerLeft, + textAlign: TextAlign.left, + text: details, + isExpanded: false, + fontSize: 10.sp, + fontColor: CustomAppColors.kBlackColor, + ), + ), + ], + ), + ); + } +} diff --git a/lib/view/screens/rota/book_holiday_screen.dart b/lib/view/screens/rota/book_holiday_screen.dart new file mode 100644 index 0000000..0e03f50 --- /dev/null +++ b/lib/view/screens/rota/book_holiday_screen.dart @@ -0,0 +1,255 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/dialogs/widgets/holiday_request_sent_dialog.dart'; +import 'package:ftc_mobile_app/dialogs/widgets/holidays_data_dialog.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/staffWorkload/StaffWorkloadResponse.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import '../../custom_widgets/label_value_box_widget.dart'; + +class BookHolidayScreen extends StatefulWidget { + const BookHolidayScreen({Key? key}) : super(key: key); + + @override + State createState() => _BookHolidayScreenState(); +} + +class _BookHolidayScreenState extends State { + final controller = Get.put(BookHolidayScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: 'Book a Holiday', + ), + body: ListView( + children: [ + Obx(() { + return InkWell( + onTap: () => _onTotalHolidaysBoxTap(controller.myWorkLoads()), + child: LabelValueBoxWidget( + label: 'Total Holidays Left', + value: + '${controller.myWorkLoads()?.holidaysRemaining ?? 0} days (${controller.myWorkLoads()?.holidayAlwnNoOfHours ?? 0} hours)', + trailing: const Icon( + Icons.arrow_right_rounded, + color: Colors.black, + ), + ).addPaddingHorizontal(16), + ); + }), + 10.verticalSpace, + Obx( + () => CalendarWidget( + markedDatesMap: controller.markedDatesMap(), + minDate: DateTime.now().subtract(10.days), + targetDateTime: controller.targetDateTime.value, + canSelectRange: true, + rangeStart: controller.holidayStartDate(), + rangeEnd: controller.holidayEndDate(), + onRangeSelect: controller.onRangeSelect, + onDayTap: (date, _) => controller.targetDateTime(date), + ), + ), + 10.verticalSpace, + Padding( + padding: REdgeInsets.symmetric(horizontal: 16.0, vertical: 10), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + //Start End date boxes + Row( + children: [ + Expanded( + child: Obx( + () => LabelValueBoxWidget( + label: "Start Date", + value: DateFormatter() + .getHolidayDate(controller.holidayStartDate()), + ), + ), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 14.r), + child: Icon( + Icons.arrow_forward, + size: 20.0.sp, + ), + ), + Expanded( + child: Obx( + () => LabelValueBoxWidget( + label: "End Date", + value: DateFormatter() + .getHolidayDate(controller.holidayEndDate()), + ), + ), + ), + ], + ), + 10.verticalSpace, + SizedBox( + width: double.infinity, + child: Obx(() => LabelValueBoxWidget( + label: "Holiday Request", + value: + "${controller.holidayDays()} Days (${controller.holidayHours()} Hours)", + ))), + 20.verticalSpace, + AgreeToRulesWidget( + agreeToRules: controller.agreeToRules, + onTapViewBookingRules: () {}, + ), + 25.verticalSpace, + RequestHolidayButton( + onTap: _onRequestHolidayButtonTap, + ), + ], + ), + ), + ], + ), + ); + } + + _onTotalHolidaysBoxTap(StaffWorkLoads? workLoads) { + DateTime date = DateTime.now(); + if (workLoads?.endDate.isNotNullOrEmpty() == true) { + date = DateTime.parse(workLoads!.endDate!); + } + + final holidayData = HolidayModel( + carriedOver: "${workLoads?.carriedOverHours ?? 0} hours", + holidayEntitlement: + "${workLoads?.holidayEntitlement?.numberOfWeeks ?? 0} weeks (${workLoads?.holidayEntitlement?.numberOfDays ?? 0} days, ${workLoads?.holidayEntitlement?.numberOfHours ?? 0} hours)", + holidayAllowance: + "${workLoads?.holidayAlwnNoOfDys ?? 0} days (${workLoads?.holidayAlwnNoOfHours ?? 0} hours)", + remainingHolidays: + "${workLoads?.holidaysRemaining ?? 0} days (${workLoads?.holidayAlwnNoOfHours ?? 0} hours)", + timeLeftBeforeYearEnd: "${date.difference(DateTime.now()).inDays} days", + ); + + showDialog( + context: context, + builder: (BuildContext context) { + return HolidaysDataDialog(holidayModel: holidayData); + }); + } + + _onRequestHolidayButtonTap() async { + final isSuccess = await controller.requestHoliday(); + + if (isSuccess) { + Get.dialog(HolidayRequestSentDialog( + holidayStartDate: + DateFormatter().getHolidayDate(controller.holidayStartDate.value), + holidayEndDate: + DateFormatter().getHolidayDate(controller.holidayEndDate.value), + holidayTotalTime: + "${controller.holidayDays()} Days (${controller.holidayHours()} Hours)", + )); + } + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} + +class AgreeToRulesWidget extends StatelessWidget { + final RxBool agreeToRules; + final VoidCallback onTapViewBookingRules; + + const AgreeToRulesWidget({ + super.key, + required this.agreeToRules, + required this.onTapViewBookingRules, + }); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + InkWell( + onTap: agreeToRules.toggle, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Obx( + () => Ink( + width: 24.r, + height: 24.r, + child: Checkbox( + value: agreeToRules.value, + activeColor: CustomAppColors.kSecondaryColor, + onChanged: (value) { + agreeToRules.value = value ?? false; + }, + ), + ), + ), + 10.horizontalSpace, + CustomTextWidget( + isExpanded: false, + text: 'Agree to the booking rules', + fontColor: Colors.black, + fontSize: 10.0.sp, + fontWeight: FontWeight.w400, + ), + ], + ), + ), + // const Spacer(), + // InkWell( + // onTap: () {}, + // child: Padding( + // padding: REdgeInsets.only(left: 8.0, top: 4.r, bottom: 4.r), + // child: CustomTextWidget( + // isExpanded: false, + // text: 'View Booking Rules', + // fontColor: CustomAppColors.kBlackColor, + // fontWeight: FontWeight.bold, + // fontSize: 10.sp, + // ), + // ), + // ), + ], + ); + } +} + +class RequestHolidayButton extends StatelessWidget { + final VoidCallback onTap; + + const RequestHolidayButton({super.key, required this.onTap}); + + @override + Widget build(BuildContext context) { + // final BookHolidayScreenController controller = + // Get.put(BookHolidayScreenController()); + return GestureDetector( + onTap: onTap, + child: Container( + width: MediaQuery.of(context).size.width, + height: 60.h, + alignment: Alignment.center, + decoration: BoxDecoration( + color: CustomAppColors.kSecondaryColor, + borderRadius: BorderRadius.circular(2.r), + ), + child: const CustomTextWidget( + text: "REQUEST HOLIDAY", fontColor: CustomAppColors.kPrimaryColor), + ), + ); + } +} diff --git a/lib/view/screens/rota/calendar_view_screen.dart b/lib/view/screens/rota/calendar_view_screen.dart new file mode 100644 index 0000000..f0966fe --- /dev/null +++ b/lib/view/screens/rota/calendar_view_screen.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; + +class CalendarScreen extends StatefulWidget { + const CalendarScreen({Key? key}) : super(key: key); + + @override + State createState() => _CalendarScreenState(); +} + +class _CalendarScreenState extends State { + final CalendarViewScreenController controller = + Get.find(); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.scaffoldKey, + onScreenTap: controller.removeFocus, + body: Obx( + () => controller.isLoading.value + ? const Center( + child: CircularProgressIndicator(), + ) + : Column( + children: [ + // Obx( + // () => SetMonthAndYearWidget( + // currentMonthName: controller.currentMonthName.value, + // nextMonthTap: controller.onNextMonthTap, + // previousMonthTap: controller.onPreviousMonthTap), + // ), + Obx( + () => CalendarWidget( + markedDatesMap: controller.events(), + targetDateTime: controller.targetDateTime.value, + onEventTap: (events) { + if (events.isNotEmpty) { + controller.selectedDate.value = + MarkDatesModel.addDate( + date: events.first.date, + title: events.first.title ?? ""); + controller.datesToShowList.value = [ + RotaShift( + name: "", + staffRequired: "", + workerType: "", + location: "", + startTime: controller.selectedDate.value.date + .toIso8601String(), + endTime: "", + breakTime: "", + notes: "") + ]; + } + }), + ), + 10.verticalSpace, + Expanded( + child: Obx( + () => ListView.builder( + itemCount: controller.datesToShowList.length, + itemBuilder: (BuildContext context, int index) { + Rx dateModel = + controller.datesToShowList[index].obs; + //Todo: RotaWidget need to reconfigure here + return SizedBox.shrink(); + // return RotaWidget( + // startDateTime: dateModel.value.shiftStartTime, + // endDateTime: dateModel.value.shiftEndTime); + }, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/view/screens/rota/export_rota_screens.dart b/lib/view/screens/rota/export_rota_screens.dart new file mode 100644 index 0000000..9b4e6fb --- /dev/null +++ b/lib/view/screens/rota/export_rota_screens.dart @@ -0,0 +1,5 @@ +export 'calendar_view_screen.dart'; +export 'rota_dashboard_screen.dart'; +export 'book_holiday_screen.dart'; +export 'pick_up_shifts_screen.dart'; +export 'your_rota_screen.dart'; \ No newline at end of file diff --git a/lib/view/screens/rota/new_rota_list_widget.dart b/lib/view/screens/rota/new_rota_list_widget.dart new file mode 100644 index 0000000..17c5f58 --- /dev/null +++ b/lib/view/screens/rota/new_rota_list_widget.dart @@ -0,0 +1,69 @@ +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/rota/WeekArrayData.dart'; + +class NewRotaListWidget extends StatelessWidget { + final List shifts; + final Function(int index, DaysArrayData data)? onClaimShiftTap; + final Function(int index, DaysArrayData data)? onCancelShiftTap; + + final ScrollPhysics? physics; + + const NewRotaListWidget({ + super.key, + required this.shifts, + this.onClaimShiftTap, + this.onCancelShiftTap, + this.physics, + }); + + @override + Widget build(BuildContext context) { + return (shifts.isEmpty) + ? FrequentFunctions.centerText(text: "No shifts found") + : ListView.separated( + shrinkWrap: true, + itemCount: shifts.length, + physics: physics, + padding: REdgeInsets.symmetric(horizontal: 20), + separatorBuilder: (_, index) => 10.verticalSpace, + itemBuilder: (_, index) { + return RotaWidget( + data: shifts[index], + onViewShiftTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return ShowRotaAlertDialog( + data: shifts[index], + onClaimShiftTap: () { + if (onClaimShiftTap != null) { + onClaimShiftTap!.call(index, shifts[index]); + } + }, + onCancelShiftTap: () { + if (onCancelShiftTap != null) { + onCancelShiftTap!.call(index, shifts[index]); + } + }, + ); + }); + + // onViewShiftTap(index, shifts[index]) + }, + ); + }, + ); + } + +// Container _upperTextWidget({required String text}) { +// return Container( +// padding: const EdgeInsets.only(left: 20), +// child: Text( +// text, +// style: const TextStyle( +// color: CustomAppColors.kLightTextColor, fontSize: 12), +// ), +// ); +// } +} diff --git a/lib/view/screens/rota/pick_up_shifts_screen.dart b/lib/view/screens/rota/pick_up_shifts_screen.dart new file mode 100644 index 0000000..0fa8c6d --- /dev/null +++ b/lib/view/screens/rota/pick_up_shifts_screen.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; +import 'new_rota_list_widget.dart'; + +class PickUpShiftsScreen extends StatefulWidget { + const PickUpShiftsScreen({Key? key}) : super(key: key); + + @override + State createState() => _PickUpShiftsScreenState(); +} + +class _PickUpShiftsScreenState extends State { + final _controller = Get.put(PickUpShiftsScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + screenKey: _controller.screenKey, + onScreenTap: _controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly(context, titleText: "Available Shifts"), + body: _getBody(), + ); + } + + Widget _getBody() { + return Obx( + () => _controller.loadingShifts() + ? const Center(child: CircularProgressIndicator()) + : NewRotaListWidget( + shifts: _controller.myShiftsList(), + onClaimShiftTap: _controller.claimShifts, + ), + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/rota/rota_dashboard_screen.dart b/lib/view/screens/rota/rota_dashboard_screen.dart new file mode 100644 index 0000000..d486d27 --- /dev/null +++ b/lib/view/screens/rota/rota_dashboard_screen.dart @@ -0,0 +1,206 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/rota/WeekArrayData.dart'; +import 'package:get/get.dart'; +import 'new_rota_list_widget.dart'; + +class RotaDashboardScreen extends StatefulWidget { + const RotaDashboardScreen({Key? key}) : super(key: key); + + @override + State createState() => _RotaDashboardScreenState(); +} + +class _RotaDashboardScreenState extends State { + final _controller = Get.put(RotaDashboardScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + screenKey: _controller.screenKey, + onScreenTap: _controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly(context, titleText: 'Rota'), + body: _tabWidget(), + ); + } + + Widget _tabWidget() { + return Column( + children: [ + Padding( + padding: EdgeInsets.symmetric( + horizontal: 18.0.w, + ), + child: Row( + children: [ + Expanded( + child: _buttons( + onTap: () { + Navigator.pushNamed( + context, + CustomRouteNames.kPickUpShiftsScreenRoute, + ); + }, + text: "Pick up Shifts")), + 24.horizontalSpace, + Expanded( + child: _buttons( + onTap: () { + Navigator.pushNamed( + context, + CustomRouteNames.kBookHolidayScreenRoute, + ); + }, + text: "Book a Holiday")), + ], + ), + ), + Container( + width: Get.width, + alignment: Alignment.center, + child: TabBar( + controller: _controller.tabController, + unselectedLabelColor: CustomAppColors.kLightGreyColor, + labelColor: CustomAppColors.kSecondaryColor, + labelPadding: const EdgeInsets.all(0), + indicatorSize: TabBarIndicatorSize.tab, + indicatorColor: CustomAppColors.kSecondaryColor, + tabs: const [ + Tab( + child: Text( + "List", + textAlign: TextAlign.center, + maxLines: 2, + ), + ), + Tab( + child: Text( + "Calendar", + textAlign: TextAlign.center, + maxLines: 2, + ), + ), + ], + //Change + ), + ), + Expanded( + child: TabBarView( + controller: _controller.tabController, + children: [_yourRotaListTabView, _calandarTabView()], + ), + ), + ], + ); + } + + Widget _calandarTabView() { + return Column( + children: [ + Flexible( + child: Obx( + () { + return CalendarWidget( + key: UniqueKey(), + markedDatesMap: _controller.markedDatesMap(), + minDate: DateTime.now().subtract(40.days), + targetDateTime: _controller.targetDateTime(), + canSelectRange: false, + onDayTap: _controller.onDateSelect, + ); + }, + ), + ), + Expanded( + child: Obx( + () => NewRotaListWidget( + shifts: _controller.dateFilteredShiftsList(), + onCancelShiftTap: (int index, DaysArrayData data) {}, + ), + ), + ), + ], + ); + } + + Widget get _yourRotaListTabView { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _yourRotaHeader(), + Expanded( + child: Obx( + () => _controller.loadingShifts() + ? const Center(child: CircularProgressIndicator()) + : NewRotaListWidget( + shifts: _controller.myShiftsList(), + onCancelShiftTap: (int index, DaysArrayData data) {}, + ), + ), + ), + ], + ); + } + + Widget _yourRotaHeader() { + return InkWell( + onTap: () { + Navigator.pushNamed(context, CustomRouteNames.kYourRotaScreenRoute); + }, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CustomTextWidget( + text: "Your Rota", + fontSize: 16.sp, + fontWeight: FontWeight.w700, + isExpanded: false, + ), + const CustomImageWidget( + imagePath: AssetsManager.kArrowNextIcon, + ), + ], + ), + ), + ); + } + + Widget _buttons({required VoidCallback onTap, required String text}) { + return InkWell( + onTap: onTap, + child: Container( + height: 42.r, + width: double.maxFinite, + padding: REdgeInsets.all(10).copyWith(right: 2.r), + decoration: BoxDecoration( + border: Border.all( + color: CustomAppColors.kSecondaryColor, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CustomTextWidget( + text: text, + isExpanded: false, + fontWeight: FontWeight.w600, + fontSize: 14.sp), + Icon( + Icons.keyboard_arrow_right, + size: 20.r, + ), + ], + ), + ), + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/rota/your_rota_screen.dart b/lib/view/screens/rota/your_rota_screen.dart new file mode 100644 index 0000000..bbd4074 --- /dev/null +++ b/lib/view/screens/rota/your_rota_screen.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/rota/WeekArrayData.dart'; +import 'package:get/get.dart'; + +import 'new_rota_list_widget.dart'; + +class YourRotaScreen extends StatefulWidget { + const YourRotaScreen({Key? key}) : super(key: key); + + @override + State createState() => _YourRotaScreenState(); +} + +class _YourRotaScreenState extends State { + final controller = Get.put(YourRotaScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBar( + showBoxShadow: false, + leadingButton: Container(), + titleWidget: Row( + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: CustomImageWidget( + imagePath: AssetsManager.kBackIcon, + height: 11.53.h, + width: 8.66.w, + ), + ), + SizedBox( + width: 15.w, + ), + CustomTextWidget( + text: "Your Rota", + isExpanded: false, + fontSize: 16.sp, + fontWeight: FontWeight.w700), + ], + ), + ), + body: _getBody(), + ); + } + + Widget _getBody() { + return Obx( + () => controller.loadingShifts() + ? const Center(child: CircularProgressIndicator()) + : NewRotaListWidget( + shifts: controller.myShiftsList(), + onCancelShiftTap: (int index, DaysArrayData data) {}, + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} + +// class UpperTextWidget extends StatelessWidget { +// const UpperTextWidget({ +// super.key, +// required this.text, +// }); +// +// final String text; +// +// @override +// Widget build(BuildContext context) { +// return Container( +// padding: const EdgeInsets.only(left: 20), +// child: Text( +// text, +// style: const TextStyle( +// color: CustomAppColors.kLightTextColor, fontSize: 12), +// ), +// ); +// } +// } diff --git a/lib/view/screens/settings/controller/settings_screen_controller.dart b/lib/view/screens/settings/controller/settings_screen_controller.dart new file mode 100644 index 0000000..2c94ccd --- /dev/null +++ b/lib/view/screens/settings/controller/settings_screen_controller.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class SettingsScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + @override + void onInit() { + super.onInit(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/view/screens/settings/settings_screen.dart b/lib/view/screens/settings/settings_screen.dart new file mode 100644 index 0000000..e5712e9 --- /dev/null +++ b/lib/view/screens/settings/settings_screen.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/dialogs/app_dialogs.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:get/get.dart'; +import '../../../ftc_mobile_app.dart'; +import 'controller/settings_screen_controller.dart'; + +class SettingsScreen extends StatefulWidget { + const SettingsScreen({Key? key}) : super(key: key); + + @override + State createState() => _SettingsScreenState(); +} + +class _SettingsScreenState extends State { + final controller = Get.put(SettingsScreenController()); + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CustomScaffold( + // onBackButton: () => controller.backButtonPressed(context), + backgroundColor: CustomAppColors.kPrimaryColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: "Settings", + ), + body: ListView( + children: [ + 16.verticalSpace, + const BuildIconButton( + iconPath: AssetsManager.kBellIcon, + text: 'Notification', + route: CustomRouteNames.kAllCareNotesScreenRoute, + arguments: null, + ), + const BuildIconButton( + iconPath: AssetsManager.kLockIcon, + text: 'Change Password', + route: CustomRouteNames.kCarePlanMenuScreenRoute, + arguments: null, + ), + 20.verticalSpace, + CustomAppButton( + buttonText: "LOGOUT", + buttonColor: CustomAppColors.kRedColor, + borderColor: CustomAppColors.kRedColor, + textColor: CustomAppColors.kPrimaryColor, + onTap: () { + AppDialog.alertAndLogout(() { + FrequentFunctions().logoutButtonPressed(context); + }); + }, + ).addPaddingHorizontal(22), + 18.verticalSpace, + ], + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/training/controller/trainings_screen_controller.dart b/lib/view/screens/training/controller/trainings_screen_controller.dart new file mode 100644 index 0000000..577e08d --- /dev/null +++ b/lib/view/screens/training/controller/trainings_screen_controller.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/training/TrainingResponseData.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import 'package:ftc_mobile_app/utilities/frequent_functions.dart'; +import 'package:ftc_mobile_app/web_services/api_services.dart'; +import 'package:get/get.dart'; + +class TrainingsScreenController extends GetxController { + final GlobalKey screenKey = GlobalKey(); + + final trainings = [].obs; + + @override + void onReady() { + super.onReady(); + + getAllTrainings(); + } + + void removeFocus() { + FocusScope.of(screenKey.currentContext!).unfocus(); + } + + getAllTrainings() async { + final resp = await ApiService().allTrainingsList().showLoader(); + + if (resp is TrainingResponseData) { + //Todo: change this static user id here + trainings.value = resp.proposedTrainings ?? []; + } else { + if (resp.isNotNullOrEmpty()) { + FrequentFunctions.showToast(message: resp); + } + } + } + + @override + void dispose() { + Get.delete(); + super.dispose(); + } +} diff --git a/lib/view/screens/training/training_detail_screen.dart b/lib/view/screens/training/training_detail_screen.dart new file mode 100644 index 0000000..c69d9d5 --- /dev/null +++ b/lib/view/screens/training/training_detail_screen.dart @@ -0,0 +1,149 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/widgets.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/models/training/TrainingResponseData.dart'; +import 'package:get/get.dart'; + +class TrainingDetailScreenArgs { + final TrainingUsers data; + + TrainingDetailScreenArgs({required this.data}); +} + +class TrainingDetailScreen extends StatefulWidget { + final TrainingDetailScreenArgs args; + + const TrainingDetailScreen({super.key, required this.args}); + + @override + State createState() => _TrainingDetailScreenState(); +} + +class _TrainingDetailScreenState extends State { + ProposedTrainings? get _data => widget.args.data.trainingId; + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kWhiteColor, + screenKey: GlobalKey(), + onScreenTap: Get.focusScope?.unfocus, + showAppBar: true, + appBar: AppBar( + automaticallyImplyLeading: false, + title: CustomTextWidget( + text: "${_data?.prpsTrgType?.capitalizeFirst ?? ""} Training", + fontColor: Colors.white, + textAlign: TextAlign.left, + ), + backgroundColor: Theme.of(context).primaryColor, + centerTitle: false, + actions: const [ + CloseButton(color: Colors.white), + ], + ), + body: SafeArea( + child: ListView( + padding: REdgeInsets.symmetric(horizontal: 20), + children: [ + 16.verticalSpace, + _headerValueWidget( + heading: 'Training Title', + value: _data?.prpsName ?? "", + ), + 16.verticalSpace, + _headerValueWidget( + heading: 'Introduction', + value: _data?.prpsDescription ?? "", + ), + 16.verticalSpace, + Row( + children: [ + Expanded( + child: _headerValueWidget( + heading: 'Training Start Date', + value: DateFormatter.dateFormatter2.format( + DateTime.fromMillisecondsSinceEpoch( + _data?.prpsTrgStartDate ?? 0)), + ), + ), + Expanded( + child: _headerValueWidget( + heading: 'Training End Date', + value: DateFormatter.dateFormatter2.format( + DateTime.fromMillisecondsSinceEpoch( + _data?.prpsTrgEndDate ?? 0)), + ), + ), + ], + ), + 16.verticalSpace, + _headerValueWidget( + heading: 'Training Registration Date', + value: DateFormatter.dateFormatter2.format( + DateTime.fromMillisecondsSinceEpoch( + _data?.prpsTrgRegisterationDate ?? 0)), + ), + 16.verticalSpace, + Row( + children: [ + Expanded( + child: _headerValueWidget( + heading: 'Training Type', + value: _data?.prpsTrgType?.capitalizeFirst ?? "", + ), + ), + 12.horizontalSpace, + Expanded( + child: _headerValueWidget( + heading: 'Training Type', + value: _data?.prpsTrgClass?.capitalizeFirst ?? "", + ), + ), + ], + ), + 16.verticalSpace, + _headerValueWidget( + heading: 'Training Status', + value: _data?.prpsTrgStatus?.capitalizeFirst ?? "", + ), + ], + ), + ), + ); + } + + Widget _headerValueWidget({required String heading, required String value}) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _heading(heading), + 3.verticalSpace, + _subtext(value), + ], + ); + } + + Widget _heading(String text) { + return Text( + text, + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w700, + color: Colors.black, + ), + ); + } + + Widget _subtext(String text) { + return Text( + text, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w400, + ), + ); + } +} diff --git a/lib/view/screens/training/training_screen.dart b/lib/view/screens/training/training_screen.dart new file mode 100644 index 0000000..0d29cf5 --- /dev/null +++ b/lib/view/screens/training/training_screen.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/training/TrainingResponseData.dart'; +import 'package:get/get.dart'; + +import 'controller/trainings_screen_controller.dart'; +import 'training_detail_screen.dart'; + +class TrainingsScreen extends StatefulWidget { + const TrainingsScreen({Key? key}) : super(key: key); + + @override + State createState() => _TrainingsScreenState(); +} + +class _TrainingsScreenState extends State { + final controller = Get.put(TrainingsScreenController()); + + @override + Widget build(BuildContext context) { + return CustomScaffold( + backgroundColor: CustomAppColors.kSmokeColor, + screenKey: controller.screenKey, + onScreenTap: controller.removeFocus, + showAppBar: true, + appBar: CustomAppBarTitleOnly( + context, + titleText: "Training", + ), + body: SafeArea( + child: _listView(), + ), + ); + } + + Widget _listView() { + return Obx(() { + final list = controller.trainings(); + return (list.isEmpty) + ? Container( + color: Colors.white, + child: const Center( + child: Text("No data found"), + ), + ) + : ListView.separated( + itemCount: list.length, + itemBuilder: (_, index) { + final item = list[index]; + return _listItem(item); + }, + separatorBuilder: (_, i) => 8.verticalSpace, + ); + }); + } + + Widget _listItem(TrainingUsers item) { + return InkWell( + onTap: () { + Get.toNamed(CustomRouteNames.kTrainingDetailScreen, + arguments: TrainingDetailScreenArgs(data: item)); + }, + child: Container( + decoration: const BoxDecoration(color: Colors.white), + padding: REdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + CustomTextWidget( + text: item.trainingId?.prpsName ?? "", + textAlign: TextAlign.left, + fontSize: 16.sp, + fontWeight: FontWeight.w500, + ), + CustomTextWidget( + text: + "Start date: ${DateFormatter.dateFormatter2.format(DateTime.fromMillisecondsSinceEpoch(item.trainingId?.prpsTrgStartDate ?? 0).toLocal())}", + fontColor: Colors.grey, + textAlign: TextAlign.left, + fontSize: 12.sp, + ), + 4.verticalSpace, + CustomTextWidget( + text: + "Status: ${item.trainingId?.prpsTrgStatus?.capitalizeFirst ?? ""}", + textAlign: TextAlign.left, + fontColor: Colors.black, + fontSize: 14.sp, + maxLines: 3, + fontWeight: FontWeight.w500, + ) + ], + ), + ), + const Icon(Icons.keyboard_arrow_right_rounded) + ], + ), + ), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/view/screens/webview/controller/webview_screen_controller.dart b/lib/view/screens/webview/controller/webview_screen_controller.dart new file mode 100644 index 0000000..cb68d9d --- /dev/null +++ b/lib/view/screens/webview/controller/webview_screen_controller.dart @@ -0,0 +1,5 @@ +import 'package:get/get.dart'; + +class WebviewScreenController extends GetxController { + final isLoading = false.obs; +} diff --git a/lib/view/screens/webview/webview_screen.dart b/lib/view/screens/webview/webview_screen.dart new file mode 100644 index 0000000..ce930b2 --- /dev/null +++ b/lib/view/screens/webview/webview_screen.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:get/get.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import 'controller/webview_screen_controller.dart'; + +class WebviewScreenArgument { + final String title; + final String url; + final bool willPop; + + WebviewScreenArgument( + {Key? key, required this.title, required this.url, this.willPop = true}); +} + +class WebviewScreen extends StatefulWidget { + final WebviewScreenArgument args; + + const WebviewScreen({Key? key, required this.args}) : super(key: key); + + @override + _WebviewScreenState createState() => _WebviewScreenState(); +} + +class _WebviewScreenState extends State { + final controller = + Get.put(WebviewScreenController()); + late final WebViewController webViewController; + + @override + void initState() { + super.initState(); + + webViewController = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setBackgroundColor(const Color(0x00000000)) + ..setNavigationDelegate( + NavigationDelegate( + onProgress: (int progress) {}, + onPageStarted: (String url) { + controller.isLoading.value = true; + }, + onPageFinished: (String url) { + controller.isLoading.value = false; + }, + onHttpError: (HttpResponseError error) {}, + onWebResourceError: (WebResourceError error) {}, + onNavigationRequest: (NavigationRequest request) { + if (request.url.startsWith('https://www.youtube.com/')) { + return NavigationDecision.prevent; + } + return NavigationDecision.navigate; + }, + ), + ) + ..loadRequest(Uri.parse(widget.args.url)); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: CustomAppBarTitleOnly( + context, + titleText: widget.args.title, + ), + body: Obx(() { + return Stack( + children: [ + Positioned.fill( + // child: WebViewWidget(controller: webController), + child: WebViewWidget(controller: webViewController), + ), + Positioned.fill( + child: (controller.isLoading()) + ? Center( + child: CircularProgressIndicator( + color: Get.theme.primaryColor)) + : FrequentFunctions.noWidget, + ), + ], + ); + }), + ); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } +} diff --git a/lib/web_services/api_services.dart b/lib/web_services/api_services.dart new file mode 100644 index 0000000..eda0961 --- /dev/null +++ b/lib/web_services/api_services.dart @@ -0,0 +1,40 @@ +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/training/TrainingResponseData.dart'; +import 'package:ftc_mobile_app/utilities/enums/api_method.dart'; + +class ApiService { + static final ApiService _instance = ApiService._private(); + + ApiService._private(); + + factory ApiService() { + return _instance; + } + + final HttpRequestClient _httpClient = HttpRequestClient(); + + Future allTrainingsList() async { + Map requestBody = { + "sortorder": -1, + "offset": 0, + "limit": 50, + "query": { + "critarion": { + "user": "658adee559e19fef22ce1a17" + } + } + }; + + ResponseModel responseModel = await _httpClient.safeApiCall( + method: ApiMethod.post, + url: WebUrls.allTrainings, + body: requestBody, + ); + + if ((responseModel.statusCode ~/ 100) == 2) { + return TrainingResponseData.fromJson(responseModel.data); + } else { + return responseModel.statusDescription; + } + } +} diff --git a/lib/web_services/auth_services.dart b/lib/web_services/auth_services.dart new file mode 100644 index 0000000..1807ab7 --- /dev/null +++ b/lib/web_services/auth_services.dart @@ -0,0 +1,130 @@ +import '../ftc_mobile_app.dart'; + +class AuthService { + static final AuthService _instance = AuthService._constructor(); + + AuthService._constructor(); + + factory AuthService() { + return _instance; + } + + final HttpRequestClient _httpClient = HttpRequestClient(); + + // final LocalStorageManager _sessionManagement = LocalStorageManager(); + + Future loginUser( + {required String email, + required String password, + bool isRememberMe = false}) async { + Map requestBody = { + "email": email, + "password": password, + "fcm_tokens": { + "token": + "fY6_DGfGQ06OW4BUVEsEwG:APA91bGUFza2uhaaR0miN2jtY0ut7RuA5ObleZvqv2X8KLOBgksmDmgc9sHDdea-DBvHmz1aUwX1uhkysk92x50WQCLHPDD1VwGX5ybKhUwVdq4aBfI24vhXqMB-FksWXEZwzDAi9BA_", + // "token": "${await FirebaseMessaging.instance.getToken()}", + "deviceType": "android" + } + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.signInUrl, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + ); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + return true; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + //verify Otp and if success storing token and user data + Future verifyOtp({required String verificationCode}) async { + Map requestBody = { + "email": LocalStorageManager.getSessionToken( + tokenKey: LocalStorageKeys.kSaveEmailKey), + "verification_code": verificationCode, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.verifyCodeUrl, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription == "Code verified successfully") { + + final data = UserModel.fromJson(responseModel.data ?? {}); + + LocalStorageManager.setLoginToken(responseModel.userToken); + LocalStorageManager.setUserId(data.id); + // await LocalStorageManager.saveSession( + // tokenKey: LocalStorageKeys.kUserModelKey, + // tokenValue: + // json.encode(UserModel.fromJson(responseModel.data ?? "").toJson()), + // ); + // FrequentFunctions.userModel.value = + // UserModel.fromJson(responseModel.data ?? ""); + return true; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future passwordLessLogin( + {required String email, bool isRememberMe = false}) async { + Map requestBody = { + "email": email, + "expiryTime": "3m", + "fcm_tokens": { + "token": + "fY6_DGfGQ06OW4BUVEsEwG:APA91bGUFza2uhaaR0miN2jtY0ut7RuA5ObleZvqv2X8KLOBgksmDmgc9sHDdea-DBvHmz1aUwX1uhkysk92x50WQCLHPDD1VwGX5ybKhUwVdq4aBfI24vhXqMB-FksWXEZwzDAi9BA_", + "deviceType": "android" + } + }; + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.passwordLessSignInUrl, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + ); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + return true; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future forgetPassword({required String email}) async { + Map requestBody = { + "email": email, + // "fcm_tokens": { + // "token": + // "fY6_DGfGQ06OW4BUVEsEwG:APA91bGUFza2uhaaR0miN2jtY0ut7RuA5ObleZvqv2X8KLOBgksmDmgc9sHDdea-DBvHmz1aUwX1uhkysk92x50WQCLHPDD1VwGX5ybKhUwVdq4aBfI24vhXqMB-FksWXEZwzDAi9BA_", + // "deviceType": "android" + // } + }; + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.forgetPasswordUrl, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + ); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + return true; + } else { + return responseModel.statusDescription; + } + } +} diff --git a/lib/web_services/chat_services.dart b/lib/web_services/chat_services.dart new file mode 100644 index 0000000..4959d6b --- /dev/null +++ b/lib/web_services/chat_services.dart @@ -0,0 +1,328 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/chat/ChatModel.dart'; +import 'package:ftc_mobile_app/models/chat/add_group_message_model.dart'; +import 'package:ftc_mobile_app/models/chat/all_group_messages_model.dart'; +import 'package:ftc_mobile_app/models/chat/all_single_chat_message_model.dart'; +import 'package:ftc_mobile_app/models/chat/combined_last_messages_model_class.dart'; +import '../models/chat/single_chat.dart'; + +enum MessageType { file, message } + +class ChatService { + static final ChatService _instance = ChatService._private(); + + ChatService._private(); + + factory ChatService() { + return _instance; + } + + final HttpRequestClient _httpClient = HttpRequestClient(); + + Future _uploadFile({ + required String filePath, + }) async { + final formData = FormData(); + formData.files.add(MapEntry( + "attachment", + MultipartFile.fromFileSync(filePath), + )); + + ResponseModel responseModel = await _httpClient.safeFormDataRequest( + url: WebUrls.uploadMessageAttachmentsURL, + body: formData, + ); + + print("responseModel is : ${responseModel.toJson()}"); + + if ((responseModel.statusCode ~/ 100) == 2) { + //Success response + return responseModel.data; // returning file path from response + } else { + return { + "message": responseModel.statusDescription, + }; + } + + // final files = {"attachment": filePath}; + // ResponseModel responseModel = await _httpClient.postMultipartRequest( + // files: files, + // url: WebUrls.uploadMessageAttachmentsURL, + // ); + // + // if (responseModel.statusCode ~/ 100 == 2) { + // print("got data as: ${responseModel.data}"); + // return responseModel.data; + // } else { + // return { + // "message": responseModel.statusDescription, + // }; + // } + } + + Future addSingleMessage({ + required String message, + required MessageType messageType, + File? file, + required String senderId, + required String receiverId, + }) async { + Map requestBody = { + "from": senderId, + "to": receiverId, + "message": message, + "messageType": messageType.name, + }; + + if (messageType == MessageType.file) { + if (file == null) { + return { + "message": "Please select file to upload", + }; + } + + final resp = await _uploadFile(filePath: file.path); + print("upload resp: $resp"); + if (resp is String) { + requestBody['message'] = resp; //overriding message in case of file send + requestBody['filePath'] = resp; + } else { + return { + "message": "Failed to send file", + }; + } + } + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.addSingleMessageChatURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription + .toLowerCase() + .contains("message added")) { + if (responseModel.data is List && responseModel.data.length > 0) { + SingleChatModelClass singleChatModelClass = + SingleChatModelClass.fromJson(responseModel.data[0]); + return singleChatModelClass; + } else { + SingleChatModelClass singleChatModelClass = + SingleChatModelClass.fromJson(responseModel.data); + return singleChatModelClass; + } + // return true; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future addGroupMessageService( + {required String message, + required MessageType messageType, + File? file, + required String groupId, + required String userId}) async { + Map requestBody = { + "groupId": groupId, + "userId": userId, + "message": message, + "messageType": messageType.name, + }; + + if (messageType == MessageType.file) { + if (file == null) { + return { + "message": "Please select file to upload", + }; + } + + final resp = await _uploadFile(filePath: file.path); + print("upload resp: $resp"); + if (resp is String) { + requestBody['message'] = resp; //overriding message in case of file send + requestBody['filePath'] = resp; + } else { + return { + "message": "Failed to send file", + }; + } + } + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.addGroupMessageServiceURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + if (responseModel.data is List && responseModel.data.length > 0) { + AddDeleteUpdateGroupMessageModel addGroupMessage = + AddDeleteUpdateGroupMessageModel.fromJson(responseModel.data[0]); + return addGroupMessage; + } else { + AddDeleteUpdateGroupMessageModel addGroupMessage = + AddDeleteUpdateGroupMessageModel.fromJson(responseModel.data); + return addGroupMessage; + } + // return true; + } else { + return responseModel.statusDescription; + } + } + + Future combinedLastMessage({required String userId}) async { + Map requestBody = { + "userId": userId, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.combinedLastMessageURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription + .toLowerCase() + .contains("group messages")) { + if (responseModel.data is List && responseModel.data.length > 0) { + CombinedMessageModel combinedMessageModel = + CombinedMessageModel.fromJson(responseModel.data[0]); + return combinedMessageModel; + } else { + CombinedMessageModel combinedMessageModel = + CombinedMessageModel.fromJson(responseModel.data); + return combinedMessageModel; + } + // return true; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future allGroupMessages({ + required int sortOrder, + required int offset, + required int limit, + required String groupId, + required bool isDeleted, + }) async { + Map requestBody = { + "sortProperty": "createdAt", + "sortOrder": sortOrder, + "offset": offset, + "limit": limit, + "query": { + "critarion": {"groupId": groupId, "isDeleted": false} + } + }; + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.allGroupMessagesURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + if (responseModel.data is List && responseModel.data.length > 0) { + List listOfMessages = []; + for (final item in responseModel.data) { + listOfMessages.add(AllGroupMessages.fromJson(item)); + } + return listOfMessages; + } else { + return []; + } + } else { + return responseModel.statusDescription; + } + } + + Future allSingleChatMessages({required String userId}) async { + Map requestBody = { + "userId": userId, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.allSingleChatMessagesURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription + .toLowerCase() + .contains("all messages load")) { + if (responseModel.data is List && responseModel.data.length > 0) { + List listOfMessages = []; + for (var item in responseModel.data) { + listOfMessages.add(AllSingleChatMessages.fromJson(item)); + } + return listOfMessages; + } else { + AllSingleChatMessages allSingleChatMessages = + AllSingleChatMessages.fromJson(responseModel.data); + return allSingleChatMessages; + } + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future allSingleUsersChatMessagesServerAdmin({ + required String to, + required String from, + }) async { + Map requestBody = { + "to": to, + "from": from, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.allSingleChatMessagesServiceAdminURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + if (responseModel.data is List && responseModel.data.length > 0) { + List listOfMessages = []; + for (final item in responseModel.data) { + listOfMessages.add(ChatModel.fromJson(item)); + } + return listOfMessages; + } else { + // AllSingleUsersChats allSingleUsersChats = + // AllSingleUsersChats.fromJson(responseModel.data); + return []; + } + } else { + return responseModel.statusDescription; + } + } +} diff --git a/lib/web_services/client_services.dart b/lib/web_services/client_services.dart new file mode 100644 index 0000000..9c4d0bd --- /dev/null +++ b/lib/web_services/client_services.dart @@ -0,0 +1,1202 @@ +import 'package:dio/dio.dart'; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:ftc_mobile_app/models/clients/PBSPlanModel.dart'; +import 'package:ftc_mobile_app/models/clients/allCareNotes/AllCareNotesListResponse.dart'; +import 'package:ftc_mobile_app/models/clients/allCareNotes/CareNoteData.dart'; +import 'package:ftc_mobile_app/models/clients/body_points_category.dart'; +import 'package:ftc_mobile_app/models/clients/consent_details_model.dart'; +import 'package:ftc_mobile_app/models/clients/memoryListResponse/MemoryListResponse.dart'; +import 'package:ftc_mobile_app/models/clients/memoryListResponse/MemoryListResponseData.dart'; +import 'package:ftc_mobile_app/models/clients/recent_incidents_model.dart'; +import 'package:ftc_mobile_app/models/clients/add_pbs_plan_model.dart'; +import 'package:ftc_mobile_app/models/clients/riskAssessmentResponse/GetRiskAssessmentResponse.dart'; +import 'package:ftc_mobile_app/models/clients/riskAssessmentResponse/RiskAssessmentData.dart'; +import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart'; +import '../models/appointmentsListResponse/AppointmentsListResponse.dart'; +import '../models/clients/HealthIssuesDetailsModel.dart'; +import '../models/clients/allClientsList/AllClientsResponse.dart'; +import '../models/clients/documents_list_model.dart'; +import '../models/create_care_plan_request.dart'; +import '../models/profile_screen_model.dart'; + +class ClientService { + static final ClientService _instance = ClientService._private(); + + ClientService._private(); + + factory ClientService() { + return _instance; + } + + final HttpRequestClient _httpClient = HttpRequestClient(); + + Future getUserDetails() async { + // String userJson = LocalStorageManager.getSessionToken( + // tokenKey: LocalStorageKeys.kUserModelKey, + // ); + // UserModel userModel = UserModel.fromJson(json.decode(userJson)); + Map requestBody = { + "sortproperty": "createdAt", + "sortorder": -1, + "offset": 0, + "limit": 1, + "query": { + "critarion": { + "active": true, + "_id": LocalStorageManager.userId, + }, + "addedby": "_id email name", + "lastModifiedBy": "_id email name" + }, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.userProfileUrl, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription.toLowerCase().contains("success")) { + final profileDataModel = ProfileDataModel.fromJson({ + 'status': "${responseModel.statusCode}", + "data": responseModel.data, + 'message': responseModel.statusDescription, + }); + // await LocalStorageManager.saveSession( + // tokenKey: LocalStorageKeys.kProfileModelKey, + // tokenValue: json.encode(profileDataModel.toJson()), + // ); + return profileDataModel; + } else { + return responseModel.statusDescription; + } + } + + Future addConsentDetails( + {required String description, required String staffId}) async { + Map requestBody = { + "staffId": staffId, + "active": true, + "description": description, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.addConsentUrl, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription + .contains("Consent Template added successfully")) { + if (responseModel.data is List && responseModel.data.length > 0) { + ConsentDetailsModel consentDetailsModel = + ConsentDetailsModel.fromJson(responseModel.data[0]); + return consentDetailsModel; + } else { + ConsentDetailsModel consentDetailsModel = + ConsentDetailsModel.fromJson(responseModel.data); + return consentDetailsModel; + } + // return true; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future getConsentDetails({required String staffId}) async { + Map requestBody = { + "sortproperty": "createdAt", + "sortorder": -1, + "staffId": staffId, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.getConsentListUrl, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription.toLowerCase().contains("success")) { + List consentList = []; + if (responseModel.data is List) { + for (var singleEntry in responseModel.data) { + consentList.add(ConsentDetailsModel.fromJson(singleEntry)); + } + } else {} + return consentList; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future deleteConsent({required String consentId}) async { + Map requestBody = { + "templateId": consentId, + }; + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.deleteConsentUrl, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription.toLowerCase() == + "template removed successfully") { + return true; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future updateConsentDetails( + {required String description, required String consentId}) async { + Map requestBody = { + "templateId": consentId, + "description": description, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.updateConsentUrl, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription == "Updated successfully") { + return true; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future getServiceUsersList({ + String searchText = "", + int limit = 20, + int offset = 0, + }) async { + Map requestBody = { + "sortproperty": "createdAt", + "sortorder": -1, + "offset": offset, + "limit": limit, + "searchText": searchText, + "query": { + "critarion": {"role": "serviceuser"} + } + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.getServiceUsersListUrl, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription.toLowerCase().contains("success")) { + final resp = AllClientsResponseData.fromJson(responseModel.data); + + // List serviceUsers = []; + // for (var singleUser in responseModel.data['serviceUsers']) { + // serviceUsers.add(ServiceUserModel.fromJson(singleUser)); + // } + return AllClientsResponse(success: true, data: resp); + } else { + return AllClientsResponse( + success: false, + message: responseModel.statusDescription, + ); + } + } + + Future getAllUsersList({ + String searchText = "", + int limit = 20, + int offset = 0, + }) async { + Map requestBody = { + "sortproperty": "createdAt", + "sortorder": -1, + "offset": 0, + "query": { + "critarion": {"active": true, "role": "staffmember"} + } + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.listAllUsers, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription.toLowerCase().contains("success")) { + final resp = AllClientsResponseData.fromJson(responseModel.data); + + // List serviceUsers = []; + // for (var singleUser in responseModel.data['serviceUsers']) { + // serviceUsers.add(ServiceUserModel.fromJson(singleUser)); + // } + return AllClientsResponse(success: true, data: resp); + } else { + return AllClientsResponse( + success: false, + message: responseModel.statusDescription, + ); + } + } + + Future getBodyPointsCategoryList() async { + Map requestBody = {}; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.getBodyPointsCategoryListURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription + .toLowerCase() + .contains("category list")) { + List bodyPointsCategoryList = []; + for (var singleUser in responseModel.data) { + bodyPointsCategoryList.add(BodyPointsCategory.fromJson(singleUser)); + } + return bodyPointsCategoryList; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future addHealthIssue({ + required String userId, + required String category, + required String healthNote, + required String complaint, + }) async { + Map requestBody = { + "userId": userId, + "category": category, + "healthNote": healthNote, + "complaint": complaint, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.addHealthIssuesURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + return HealthIssueDetailsModel.fromJson(responseModel.data); + } else { + return responseModel.statusDescription; + } + } + + Future getHealthIssues({required String userId}) async { + // String userJson = _sessionManagement.getSessionToken( + // tokenKey: SessionKeys.kUserModelKey, + // ); + // UserModel userModel = UserModel.fromJson(json.decode(userJson)); + + Map requestBody = { + "userId": userId, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.getHealthIssuesListURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription + .toLowerCase() + .contains("health issues list")) { + List healthIssuesDetailsList = []; + for (var singleHealthIssue in responseModel.data) { + healthIssuesDetailsList + .add(HealthIssueDetailsModel.fromJson(singleHealthIssue)); + } + return healthIssuesDetailsList; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future updateHealthIssueData({ + required String issueId, + required String categoryId, + bool? status, + String? complaint, + String? healthNote, + }) async { + Map requestBody = { + "issueId": issueId, + "category": categoryId, + }; + + if (status != null) { + requestBody['status'] = status; + } + + if (complaint.isNotNullOrEmpty()) { + requestBody['complaint'] = complaint; + } + + if (healthNote.isNotNullOrEmpty()) { + requestBody['healthNote'] = healthNote; + } + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.updateHealthIssueURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + return BodyPointsCategory.fromJson(responseModel.data); + } else { + return responseModel.statusDescription; + } + } + + Future healthIssueChildCategory({required String categoryId}) async { + Map requestBody = { + "categoryId": categoryId, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.getHealthIssueChildCategoryListURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription.toLowerCase().contains("success")) { + List subCatList = []; + for (var singleSubCat in responseModel.data) { + subCatList.add(SubCat.fromJson(singleSubCat)); + } + return subCatList; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future addRecentIncidentService( + {required String userId, + required String incidentTitle, + required String note, + required int incidentDate}) async { + Map requestBody = { + "userId": userId, + "note": note, + "incidentTitle": incidentTitle, + "incidentDate": incidentDate, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.addRecentIncidentServiceURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription.toLowerCase().contains("success")) { + RecentIncidentsModel recentIncidentsModel = + RecentIncidentsModel.fromJson(responseModel.data); + return recentIncidentsModel; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future getRecentIncidentsListService( + {required String userId}) async { + Map requestBody = { + "query": { + "critarion": { + "userId": userId, + "active": true, + }, + }, + "sortProperty": "createdAt", + "sortOrder": -1, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.getRecentIncidentsListServiceURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription.toLowerCase().contains("success")) { + List recentIncidentsModelList = []; + if (responseModel.data is List) { + for (var data in responseModel.data) { + recentIncidentsModelList.add(RecentIncidentsModel.fromJson(data)); + } + return recentIncidentsModelList; + } else { + return responseModel.statusDescription; + } + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future updateRecentIncidentService({ + required String incidentId, + required String incidentTitle, + required String incidentNote, + required int incidentDate, + }) async { + Map requestBody = { + "id": incidentId, + "incidentDate": incidentDate, + "incidentTitle": incidentTitle, + "note": incidentNote, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.updateRecentIncidentServiceURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription.toLowerCase().contains("success")) { + if (responseModel.data is Map) { + return RecentIncidentsModel.fromJson(responseModel.data); + } else { + return responseModel.statusDescription; + } + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future deleteRecentIncident({required String incidentId}) async { + Map requestBody = { + "id": incidentId, + }; + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.deleteRecentIncidentUrl, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription.toLowerCase().contains("success")) { + if (responseModel.data is Map) { + return RecentIncidentsModel.fromJson(responseModel.data); + } else { + return responseModel.statusDescription; + } + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future addPbsPlanService( + {required String userId, + required String staffId, + required String postIncidentSupport, + required String reactiveStartegies, + required String secondaryPrevention, + required String managementOfBehaviorPlan, + required String aboutPlan}) async { + Map requestBody = { + "userId": userId, + "staffId": staffId, + "postIncidentSupport": postIncidentSupport, + "reactiveStartegies": reactiveStartegies, + "secondaryPrevention": secondaryPrevention, + "managementOfBehaviorPlan": managementOfBehaviorPlan, + "aboutPlan": aboutPlan, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.addPbsPlanServiceURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription.toLowerCase().contains("added")) { + print( + '---------data is not null returning PBSListDataJson model from service'); + AddPBSPlanModel addPBSPlanModel = + AddPBSPlanModel.fromJson(responseModel.data); + return addPBSPlanModel; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future updatePbsPlanService( + {required String id, + required String userId, + required String staffId, + required String postIncidentSupport, + required String reactiveStartegies, + required String secondaryPrevention, + required String managementOfBehaviorPlan, + required String aboutPlan}) async { + Map requestBody = { + "userId": userId, + "staffId": staffId, + "postIncidentSupport": postIncidentSupport, + "reactiveStartegies": reactiveStartegies, + "secondaryPrevention": secondaryPrevention, + "managementOfBehaviorPlan": managementOfBehaviorPlan, + "aboutPlan": aboutPlan, + "id": id, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.updatePbsPlanServiceURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription + .toLowerCase() + .contains("pbsplan updated")) { + return AddPBSPlanModel.fromJson(responseModel.data); + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future getPbsPlanListService({required String userId}) async { + Map requestBody = { + "query": { + "critarion": {"userId": userId} + }, + "sortProperty": "createdAt", + "sortOrder": -1 + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.getPbsPlanServiceURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription + .toLowerCase() + .contains("pbs plan list")) { + PBSListDataJson pbsDataJsonModel = PBSListDataJson.empty(); + if (responseModel.data != null) { + pbsDataJsonModel = PBSListDataJson.fromJson(responseModel.data); + return pbsDataJsonModel; + } else { + return responseModel.statusDescription; + } + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future deletePbsPlanService({required String pbsId}) async { + Map requestBody = { + "pbsId": pbsId, + }; + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.deletePbsPlanServiceURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription.toLowerCase().contains("deleted")) { + return true; + // if(responseModel.data is Map){ + // return RecentIncidentsModel.fromJson(responseModel.data); + // } + // else{ + // return responseModel.statusDescription; + // } + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future getDocumentListService( + {required String serviceUserId}) async { + Map requestBody = { + "sortProperty": "createdAt", + "sortOrder": -1, + // "offset": 0, + // "limit": 10, + "query": { + "critarion": {"userId": serviceUserId} + } + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.getDocumentsListServiceURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription + .toLowerCase() + .contains("document list")) { + if (responseModel.data != null) { + return DocumentsListModel.fromJson(responseModel.data); + } else { + return DocumentsListModel.empty(); + } + } else { + return responseModel.statusDescription; + } + } + + Future addDocumentService({ + required String userId, + required String docPath, + required String docDetails, + required String title, + required String addedBy, + }) async { + if (docPath.isEmpty) { + return "Please select document file first"; + } + + final resp = await uploadDocumentService(docPath: docPath); + + if (resp is! String) { + return; + } + + Map requestBody = { + "userId": userId, + "docPath": resp, + "details": docDetails, + "title": title, + "addedBy": addedBy, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.addDocumentServiceURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + (responseModel.statusDescription + .toLowerCase() + .contains("dcoument added") || + responseModel.statusDescription + .toLowerCase() + .contains("document added"))) { + print( + '---------data is not null returning Dcoument Added model from service'); + DocumentModel documentModel = DocumentModel.fromJson(responseModel.data); + return documentModel; + } else { + responseModel.statusDescription; + } + } + + Future updateDocumentService({ + required String userId, + required String docId, + required String docPath, + required String docDetails, + required String title, + required String addedBy, + }) async { + Map requestBody = { + "userId": userId, + "documentId": docId, + "docPath": docPath, + "details": docDetails, + "title": title, + "addedBy": addedBy, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.updateDocumentServiceURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + return DocumentModel.fromJson(responseModel.data); + } else { + return responseModel.statusDescription; + } + } + + Future deleteDocumentService({required String docId}) async { + Map requestBody = { + "documentId": docId, + }; + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.deleteDocServiceURL, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && + responseModel.statusCode <= 230 && + responseModel.statusDescription + .toLowerCase() + .contains("document removed successfully")) { + if (responseModel.data['deletedCount'] == 0) { + return true; + } else { + return false; + } + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future uploadDocumentService({ + required String docPath, + }) async { + final files = {"attachements": docPath}; + ResponseModel responseModel = await _httpClient.postMultipartRequest( + files: files, + url: WebUrls.uploadDocServiceURL, + ); + + if (responseModel.statusDescription + .toLowerCase() + .contains("file uploaded successfully")) { + return responseModel.data[0] + ["fileNameInDirectory"]; // returning file path from response + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future createCarePlan({ + required CreateCarePlanRequest request, + }) async { + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.createCarePlanURL, + requestBody: request.toJson(), + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + return responseModel; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future getCarePlansList( + {required String serviceUserId, int limit = 20, int offset = 0}) async { + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.getCarePlansListURL, + requestBody: { + "query": { + "critarion": { + "userId": serviceUserId, + "active": true + } + }, + "sortproperty": "eventDateTime", + "sortorder": -1, + "limit": limit, + "offset": offset + }, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + print("getCarePlansList responseModel: ${responseModel.toJson()}"); + return AllCareNotesListResponse.success() + ..message = responseModel.statusDescription + ..data = CareNoteData.fromJson(responseModel.data); + } else { + return responseModel.statusDescription; + } + } + + Future getAppointmentsList( + {required String serviceId, + required int startDate, + required int endDate}) async { + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.getAppointmentsByDate, + requestBody: { + "userId": serviceId, + "startDate": startDate, + "endDate": endDate, + }, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + if (responseModel.data is List && + (responseModel.data as List).isNotNullOrEmpty()) { + return AppointmentsListResponse( + success: true, + data: (responseModel.data as List) + .map((e) => AppointmentsListResponseData.fromJson(e)) + .toList()); + } + + return AppointmentsListResponse(success: true, data: []); + // print("getCarePlansList responseModel: ${responseModel.toJson()}"); + // return AllCareNotesListResponse.success() + // ..message = responseModel.statusDescription + // ..data = CareNoteData.fromJson(responseModel.data); + } else { + return AppointmentsListResponse( + success: false, message: responseModel.statusDescription); + } + } + + Future getRiskAssessments( + {required String serviceUserId}) async { + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.getRiskAssesments, + requestBody: {"userId": serviceUserId}, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + if (responseModel.data is List && + (responseModel.data as List).isNotNullOrEmpty()) { + return GetRiskAssessmentResponse( + success: true, + data: (responseModel.data as List) + .map((e) => RiskAssessmentData.fromJson(e)) + .toList()); + } + + return GetRiskAssessmentResponse(success: true, data: []); + } else { + return GetRiskAssessmentResponse( + success: false, message: responseModel.statusDescription); + } + } + + Future getMemoryList( + {required String serviceUserId}) async { + final req = { + "query": { + "critarion": {"userId": serviceUserId} + }, + "sortproperty": "createdAt", + "sortorder": -1 + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.getMemoryList, + requestBody: req, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + if (responseModel.data is Map) { + return MemoryListResponse( + success: true, + data: MemoryListResponseData.fromJson(responseModel.data)); + } + + return MemoryListResponse(success: false); + } else { + return MemoryListResponse( + success: false, message: responseModel.statusDescription); + } + } + + Future createRiskAssesments( + {required RiskAssessmentData data}) async { + final req = { + "userId": data.userId, + "hazard": data.hazard, + "personsExposedToHazard": data.personsExposedToHazard, + "riskIdentified": data.riskIdentified, + "coldMeasureRequired": data.coldMeasureRequired, + "pureRiskRating": { + "c": data.pureRiskRating?.c ?? 1, + "l": "${data.pureRiskRating?.l ?? 1}", + "r": "${data.pureRiskRating?.r ?? 1}", + }, + "inPlace": { + "y": "${data.inPlace?.y ?? 1}", + "n": "${data.inPlace?.n ?? 1}", + }, + "residualRiskRating": { + "c": "${data.residualRiskRating?.c ?? 1}", + "l": "${data.residualRiskRating?.l ?? 1}", + "r": "${data.residualRiskRating?.r ?? 1}", + } + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.createRiskAssesments, + requestBody: req, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + + if ((responseModel.statusCode ~/ 100) == 2) { + return true; + } else { + return responseModel.statusDescription; + } + } + + Future uploadMemoryBoxFile({ + required String filePath, + }) async { + final formData = FormData(); + formData.files.add(MapEntry( + "file", + MultipartFile.fromFileSync(filePath), + )); + + ResponseModel responseModel = await _httpClient.safeFormDataRequest( + url: WebUrls.uploadMemoryBoxFile, + body: formData, + ); + + print("responseModel is : ${responseModel.toJson()}"); + + if ((responseModel.statusCode ~/ 100) == 2) { + //Success response + return responseModel.data; // returning file path from response + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + //return true if success else error message string + Future addMemoryBoxFile({ + required String userId, + required String filePath, + required String noteDetails, + }) async { + if (filePath.isEmpty) { + return "Please select file first"; + } + + final resp = await uploadMemoryBoxFile(filePath: filePath); + if (resp is Map) { + return resp['message']; + } + + // String userJson = LocalStorageManager.getSessionToken( + // tokenKey: LocalStorageKeys.kUserModelKey, + // ); + // UserModel userModel = UserModel.fromJson(json.decode(userJson)); + + Map requestBody = { + "addedBy": LocalStorageManager.userId, + "userId": userId, + "filePath": resp, + "note": noteDetails, + }; + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.addMemoryBox, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + // DocumentModel documentModel = DocumentModel.fromJson(responseModel.data); + return true; + } else { + responseModel.statusDescription; + } + } + + Future updateMemoryBoxFile({ + required String userId, + required String id, + required String filePath, + required String noteDetails, + }) async { + dynamic resp; + + if (filePath.isNotEmpty) { + //upload file + resp = await uploadMemoryBoxFile(filePath: filePath); + + // On error + if (resp is Map) { + return resp['message']; + } + } + + // String userJson = LocalStorageManager.getSessionToken( + // tokenKey: LocalStorageKeys.kUserModelKey, + // ); + // UserModel userModel = UserModel.fromJson(json.decode(userJson)); + + Map requestBody = { + "addedBy": LocalStorageManager.userId, + "userId": userId, + "memoryBoxId": id, + }; + + if (resp is String) { + requestBody['filePath'] = resp; + } + + if (noteDetails.isNotEmpty) { + requestBody['note'] = noteDetails; + } + + ResponseModel responseModel = await _httpClient.customRequest( + "POST", + url: WebUrls.updateMemoryBox, + requestBody: requestBody, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + // return DocumentModel.fromJson(responseModel.data); + return true; + } else { + return responseModel.statusDescription; + } + } +} diff --git a/lib/web_services/export_web_services.dart b/lib/web_services/export_web_services.dart new file mode 100644 index 0000000..891c2ee --- /dev/null +++ b/lib/web_services/export_web_services.dart @@ -0,0 +1,7 @@ +export 'auth_services.dart'; +export 'client_services.dart'; +export 'message_services.dart'; +export 'notification_services.dart'; +export 'rota_services.dart'; +export 'http_request_client.dart'; +export 'web_url.dart'; \ No newline at end of file diff --git a/lib/web_services/http_request_client.dart b/lib/web_services/http_request_client.dart new file mode 100644 index 0000000..6daa6de --- /dev/null +++ b/lib/web_services/http_request_client.dart @@ -0,0 +1,774 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:developer'; +import 'dart:io'; +import 'dart:typed_data'; +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:ftc_mobile_app/utilities/enums/api_method.dart'; +import 'package:http/http.dart' as http; +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; +import 'package:http_parser/src/media_type.dart'; + +import '../dialogs/app_dialogs.dart'; +import 'logging_interceptor.dart'; + +class HttpRequestClient { + HttpRequestClient._(); + + static final HttpRequestClient _instance = HttpRequestClient._(); + + factory HttpRequestClient() { + return _instance; + } + + static const int _kSecondsTimeout = 60; + static const String _kTimeOutMessage = "Unable to process request"; + static const String _kInternetIssue = + "Your internet connection is not stable"; + static const String _kOtherException = "Unable to process request"; + + Map errorResponse(String message) { + return Map.of({"success": false, "message": message}); + } + + Map _dioErrorHandler(DioException e) { + if (e.type == DioExceptionType.connectionError) { + return errorResponse(_kInternetIssue); + } + + // The request was made and the server responded with a status code + // that falls out of the range of 2xx and is also not 304. + if (e.response != null) { + debugPrint('Dio error!'); + debugPrint('STATUS: ${e.response?.statusCode}'); + debugPrint('DATA: ${e.response?.data}'); + debugPrint('HEADERS: ${e.response?.headers}'); + + if (e.response!.statusCode == HttpStatus.unauthorized || + e.response!.statusCode == HttpStatus.forbidden) { + AppDialog.showUnauthorizedAlert(); + return errorResponse(''); + } + + if (e.response!.data != null && + e.response!.data is Map && + (e.response!.data as Map).containsKey('message')) { + return errorResponse(e.response!.data['message'].toString()); + } + + // if (e.response!.statusCode == 401) { + // return decoder(errorResponse(e.response!.data.toString())); + // } + } else { + // Error due to setting up or sending the request + debugPrint('Error sending request!'); + debugPrint(e.message); + } + + debugPrint('Dio ERROR ${e.error}\n'); + + return errorResponse(_kOtherException); + } + + Map exceptionHandler(e) { + debugPrint("Web Error: $e"); + debugPrint("e Type: ${e.runtimeType.toString()}"); + if (e is SocketException) { + return errorResponse(_kInternetIssue); + } else if (e is TimeoutException) { + return errorResponse(_kInternetIssue); + } else if (e is FormatException) { + return errorResponse(_kOtherException); + } else { + return errorResponse(_kOtherException); + } + } + + //---------------------------------------------------------------- + Future getRequestWithOutHeader({required String url}) async { + try { + http.Response response = await http + .get( + Uri.parse(url), + ) + .timeout( + const Duration( + seconds: _kSecondsTimeout, + ), + ); + ResponseModel responseModel = ResponseModel(); + if (response.body.isNotEmpty && response.body.length > 4) { + responseModel.statusCode = response.statusCode; + responseModel.statusDescription = "Success"; + responseModel.data = response.body; + } + // log('----------------response model is---------------${responseModel.toString()}'); + return responseModel; + } on TimeoutException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 408, + statusDescription: _kTimeOutMessage, + data: e.toString(), + ), + ); + } on SocketException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 400, + statusDescription: _kInternetIssue, + data: e.toString(), + ), + ); + } catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 500, + statusDescription: _kOtherException, + data: e.toString(), + ), + ); + } + } + + Future getRequestWithRequestBody({ + required String url, + dynamic requestBody, + Map? requestHeader, + }) async { + try { + Map header = {}; + if (requestHeader == null) { + header = await getRequestHeader(); + } + final uri = Uri.parse(url); + final uriWithBody = uri.replace( + queryParameters: requestBody, + ); + http.Response response = await http + .get( + uriWithBody, + headers: requestHeader ?? header, + ) + .timeout( + const Duration( + seconds: _kSecondsTimeout, + ), + ); + ResponseModel responseModel = ResponseModel(); + if ((response.statusCode >= 200 && response.statusCode <= 230)) { + responseModel = ResponseModel.fromJson( + jsonDecode(response.body), + statusCode: response.statusCode, + ); + } else { + responseModel = ResponseModel.errorFromJson( + jsonDecode(response.body), + statusCode: response.statusCode, + ); + } + // log('----------------response model is---------------${responseModel.toString()}'); + return responseModel; + } on TimeoutException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 408, + statusDescription: _kTimeOutMessage, + data: e.toString(), + ), + ); + } on SocketException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 400, + statusDescription: _kInternetIssue, + data: e.toString(), + ), + ); + } catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 500, + statusDescription: _kOtherException, + data: e.toString(), + ), + ); + } + } + + Future postRequest({ + required String url, + dynamic requestBody, + bool doJsonEncodeRequestBody = false, + bool isTokenRequired = false, + dynamic requestHeader, + }) async { + try { + ResponseModel responseModel = ResponseModel(); + Map header = {}; + if (isTokenRequired) { + header = await getRequestHeader(isBearer: true); + } + http.Response response = await http + .post( + Uri.parse(url), + body: + doJsonEncodeRequestBody ? jsonEncode(requestBody) : requestBody, + headers: isTokenRequired ? header : requestHeader, + ) + .timeout( + const Duration( + seconds: _kSecondsTimeout, + ), + ); + if ((response.statusCode >= 200 && response.statusCode <= 230)) { + responseModel = ResponseModel.fromJson( + jsonDecode(response.body), + statusCode: response.statusCode, + ); + } else { + responseModel = ResponseModel.errorFromJson( + jsonDecode(response.body), + statusCode: response.statusCode, + ); + } + // log('----------------response model is---------------${responseModel.toString()}'); + return responseModel; + } on TimeoutException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 408, + statusDescription: _kTimeOutMessage, + data: e.toString(), + ), + ); + } on SocketException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 400, + statusDescription: _kInternetIssue, + data: e.toString(), + ), + ); + } catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 500, + statusDescription: _kOtherException, + data: e.toString(), + ), + ); + } + } + + Future uploadImageRequest({ + required String url, + required String filePath, + }) async { + try { + ResponseModel responseModel = ResponseModel(); + Map header = {}; + header = await getRequestHeader(isBearer: true); + http.MultipartRequest request = http.MultipartRequest( + "POST", + Uri.parse(url), + ); + request.files.add( + await http.MultipartFile.fromPath( + "photo", + filePath, + ), + ); + request.headers.addAll(header); + http.StreamedResponse response = await request.send().timeout( + const Duration( + seconds: _kSecondsTimeout, + ), + ); + if ((response.statusCode >= 200 && response.statusCode <= 230)) { + responseModel = ResponseModel.fromJson( + jsonDecode(await response.stream.bytesToString()), + statusCode: response.statusCode, + ); + } else { + responseModel = ResponseModel.errorFromJson( + jsonDecode(await response.stream.bytesToString()), + statusCode: response.statusCode, + ); + } + // log('----------------response model is---------------${responseModel.toString()}'); + return responseModel; + } on TimeoutException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 408, + statusDescription: _kTimeOutMessage, + data: e.toString(), + ), + ); + } on SocketException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 400, + statusDescription: _kInternetIssue, + data: e.toString(), + ), + ); + } catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 500, + statusDescription: _kOtherException, + data: e.toString(), + ), + ); + } + } + + Future patchRequest({ + required String url, + dynamic requestBody, + bool isBearerHeaderRequired = false, + bool isContentType = true, + }) async { + try { + ResponseModel responseModel = ResponseModel(); + Map header = {}; + if (isBearerHeaderRequired) { + header = await getRequestHeader( + isBearer: true, + isContentType: isContentType, + ); + } + http.Request request = http.Request( + 'PATCH', + Uri.parse(url), + ); + request.body = jsonEncode(requestBody); + request.headers.addAll(header); + http.StreamedResponse response = await request.send().timeout( + const Duration( + seconds: _kSecondsTimeout, + ), + ); + if ((response.statusCode >= 200 && response.statusCode <= 230)) { + responseModel = ResponseModel.fromJson( + jsonDecode(await response.stream.bytesToString()), + statusCode: response.statusCode, + ); + } else { + responseModel = ResponseModel.errorFromJson( + jsonDecode(await response.stream.bytesToString()), + statusCode: response.statusCode, + ); + } + // log('----------------response model is---------------${responseModel.toString()}'); + return responseModel; + } on TimeoutException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 408, + statusDescription: _kTimeOutMessage, + data: e.toString(), + ), + ); + } on SocketException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 400, + statusDescription: _kInternetIssue, + data: e.toString(), + ), + ); + } catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 500, + statusDescription: _kOtherException, + data: e.toString(), + ), + ); + } + } + + Future deleteRequest({ + required String url, + bool isBearerHeaderRequired = false, + dynamic requestBody, + }) async { + try { + ResponseModel responseModel = ResponseModel(); + Map header = {}; + if (isBearerHeaderRequired) { + header = await getRequestHeader(isBearer: true); + } + http.Request request = http.Request( + 'DELETE', + Uri.parse(url), + ); + request.headers.addAll(header); + if (requestBody != null) { + request.body = jsonEncode(requestBody); + } + http.StreamedResponse response = await request.send().timeout( + const Duration( + seconds: _kSecondsTimeout, + ), + ); + if ((response.statusCode >= 200 && response.statusCode <= 230)) { + responseModel = ResponseModel.fromJson( + jsonDecode(await response.stream.bytesToString()), + statusCode: response.statusCode, + ); + } else { + responseModel = ResponseModel.errorFromJson( + jsonDecode(await response.stream.bytesToString()), + statusCode: response.statusCode, + ); + } + // log('----------------response model is---------------${responseModel.toString()}'); + return responseModel; + } on TimeoutException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 408, + statusDescription: _kTimeOutMessage, + data: e.toString(), + ), + ); + } on SocketException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 400, + statusDescription: _kInternetIssue, + data: e.toString(), + ), + ); + } catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 500, + statusDescription: _kOtherException, + data: e.toString(), + ), + ); + } + } + + Future customRequest( + String requestType, { + required String url, + bool isBearerHeaderRequired = false, + bool isBearer = true, + dynamic requestBody, + dynamic requestHeader, + }) async { + try { + ResponseModel responseModel = ResponseModel(); + Map header = {}; + if (isBearerHeaderRequired) { + header = await getRequestHeader(isBearer: isBearer); + } + http.Request request = http.Request( + requestType, + Uri.parse(url), + ); + request.headers.addAll(isBearerHeaderRequired ? header : requestHeader); + request.body = json.encode(requestBody); + log('--------------request.url----------------------------($requestType) ${request.url.origin}${request.url.path}'); + log('--------------request.headers----------------------------${request.headers}'); + log('--------------request.body----------------------------${request.body}'); + http.StreamedResponse response = await request.send().timeout( + const Duration( + seconds: _kSecondsTimeout, + ), + ); + if ((response.statusCode >= 200 && response.statusCode <= 230)) { + responseModel = ResponseModel.fromJson( + jsonDecode(await response.stream.bytesToString()), + statusCode: response.statusCode, + ); + log("Api Response($url):\n${jsonEncode(responseModel.toJson())}"); + } else { + responseModel = ResponseModel.errorFromJson( + jsonDecode(await response.stream.bytesToString()), + statusCode: response.statusCode, + ); + print("FUTURE Expection"); + print(responseModel); + } + // log('----------------response model is---------------${responseModel.toString()}'); + return responseModel; + } on TimeoutException catch (e) { + print("TimeOut Expection"); + print(e); + return Future.value( + ResponseModel.named( + statusCode: 408, + statusDescription: _kTimeOutMessage, + data: e.toString(), + ), + ); + } on SocketException catch (e) { + print("SOCKET Expection"); + print(e); + return Future.value( + ResponseModel.named( + statusCode: 400, + statusDescription: _kInternetIssue, + data: e.toString(), + ), + ); + } catch (e) { + print("FUTURE Expection"); + print(e); + return Future.value( + ResponseModel.named( + statusCode: 500, + statusDescription: _kOtherException, + data: e.toString(), + ), + ); + } + } + + Future uploadVideoRequest({ + required String uploadUrl, + required Uint8List chunkStream, + }) async { + try { + ResponseModel responseModel = ResponseModel(); + http.StreamedRequest request = http.StreamedRequest( + "PUT", + Uri.parse(uploadUrl), + ); + request.headers.addAll({ + 'Content-Length': "${chunkStream.length}", + 'Content-Type': 'video/mp4', + 'content-type': 'application/octet-stream' + }); + request.sink.add(chunkStream); + request.sink.close(); + http.StreamedResponse response = await request.send(); + responseModel.statusCode = response.statusCode; + responseModel.statusDescription = response.reasonPhrase ?? ''; + responseModel.header = response.headers; + return responseModel; + } on TimeoutException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 408, + statusDescription: _kTimeOutMessage, + data: e.toString(), + ), + ); + } on SocketException catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 400, + statusDescription: _kInternetIssue, + data: e.toString(), + ), + ); + } catch (e) { + return Future.value( + ResponseModel.named( + statusCode: 500, + statusDescription: _kOtherException, + data: e.toString(), + ), + ); + } + } + + Future> getRequestHeader({ + bool isBearer = true, + bool isContentType = true, + }) async { + // String token = SessionManagement().getSessionToken( + // tokenKey: SessionKeys.kUserTokenKey, + // ); + //todo remove this in end + String token = + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhIjp0cnVlLCJuIjoiSmFtc2hhaWQgU2FiaXIiLCJlIjoiamFtc2hhaWRzYWJpcjQxMTk4MEBnbWFpbC5jb20iLCJkIjoiNjQ2NTEyN2E3MmJjNzEzY2E4NDYwY2IzIiwicCI6Ii91cGxvYWRzL2RwL2RlZmF1bHQucG5nIiwiciI6Il9hIiwiaWF0IjoxNjkzMjIzNTIzfQ.EtpS_o0kEhdlNzCyFdFNNQzHd0IipTw8BEuUBVgfYok'; + Map header = { + 'Authorization': isBearer ? 'Bearer $token' : token, + if (isContentType) 'Content-Type': 'application/json' + }; + return header; + } + + Future safeApiCall({ + required ApiMethod method, + required String url, + Map? headers, + Map? body, + Map? param, + }) async { + try { + // final hasInternet = await FrequentFunctions.hasInternetConnection; + // debugPrint("hasInternet: $hasInternet"); + // + // if (!hasInternet) { + // return decoder(errorResponse(errorMsgNoInternet)); + // } + + Map customHeader = await getRequestHeader(); + if(headers != null) customHeader.addAll(headers!); + + final dio = Dio() + ..interceptors.add(Logging()) + ..options.headers.addAll(customHeader); + + if (headers != null) { + dio.options.headers.addAll(headers); + } + + Response response; + switch (method) { + case ApiMethod.get: + response = await dio.get(url, queryParameters: param); + break; + case ApiMethod.post: + response = await dio.post( + url, + data: (body == null) ? null : json.encode(body), + queryParameters: param, + ); + break; + case ApiMethod.put: + response = await dio.put( + url, + data: (body == null) ? null : json.encode(body), + queryParameters: param, + ); + break; + case ApiMethod.patch: + response = await dio.patch( + url, + data: (body == null) ? null : json.encode(body), + queryParameters: param, + ); + break; + case ApiMethod.delete: + response = await dio.delete( + url, + data: (body == null) ? null : json.encode(body), + queryParameters: param, + ); + break; + } + + return ResponseModel.fromJson(jsonDecode(response.toString())) + ..statusCode = response.statusCode ?? 0; + + } on DioException catch (e) { + return ResponseModel.named( + statusCode: e.response?.statusCode ?? 0, + statusDescription: _dioErrorHandler(e)['message'], + ); + } catch (e) { + debugPrint("Web Error: $e"); + + return ResponseModel.named( + statusCode: 0, + statusDescription: exceptionHandler(e)['message'], + ); + } + } + + Future safeFormDataRequest({ + required String url, + required FormData body, + }) async { + try { + Map customHeader = await getRequestHeader(); + + final dio = Dio() + ..interceptors.add(Logging()) + ..options.headers.addAll(customHeader); + + final response = await dio.post( + url, + data: body, + onSendProgress: (int sent, int total) { + debugPrint('$sent $total'); + }, + ); + + return ResponseModel.fromJson(jsonDecode(response.toString())) + ..statusCode = response.statusCode ?? 0; + } on DioException catch (e) { + return ResponseModel.named( + statusCode: e.response?.statusCode ?? 0, + statusDescription: _dioErrorHandler(e)['message'], + ); + } catch (e) { + debugPrint("Web Error: $e"); + + return ResponseModel.named( + statusCode: 0, + statusDescription: exceptionHandler(e)['message'], + ); + } + } + + Future postMultipartRequest( + {required String url, + Map fields = const {}, + Map files = const {}}) async { + try { + Map customHeader = await getRequestHeader(); + // customHeader['Connection'] = 'keep-alive'; + // customHeader['Accept'] = 'application/json'; + // customHeader['Content-Type'] = 'multipart/form-data'; + http.MultipartRequest request = + http.MultipartRequest('POST', Uri.parse(url)); + + request.headers.addAll(customHeader); + request.fields.addAll(fields); + + for (MapEntry file in files.entries) { + String type = file.value.substring(file.value.lastIndexOf('.') + 1); + String name = file.value.substring(file.value.lastIndexOf('/') + 1); + + request.files.add(await http.MultipartFile.fromPath( + file.key, + file.value, + filename: name, + contentType: MediaType('image', type), + )); + } + + http.StreamedResponse streamedResponse = await request.send(); + http.Response httpResponse = + await http.Response.fromStream(streamedResponse); + log('────────────────────url> $url'); + log('────────────────────files> $files'); + log('────────────────────fields> $fields'); + log('────────────────────Response.body> ${httpResponse.body}'); + ResponseModel response = + ResponseModel.fromJson(jsonDecode(httpResponse.body)); + return Future.value(response); + } on HttpException catch (e) { + return Future.value(ResponseModel.named( + statusCode: 405, statusDescription: e.message, data: e.toString())); + } on TimeoutException { + return Future.value(ResponseModel.named( + statusCode: 408, + statusDescription: "Request TimeOut", + data: "Request TimeOut")); + } on SocketException { + return Future.value(ResponseModel.named( + statusCode: 400, + statusDescription: "Bad Request", + data: "Bad Request")); + } catch (e) { + return Future.value(ResponseModel.named( + statusCode: 500, + statusDescription: "Service Error", + data: "Service Error")); + } + } +} diff --git a/lib/web_services/logging_interceptor.dart b/lib/web_services/logging_interceptor.dart new file mode 100644 index 0000000..502331f --- /dev/null +++ b/lib/web_services/logging_interceptor.dart @@ -0,0 +1,103 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; + +class Logging extends Interceptor { + // String get _token => LocalStorage.token ?? emptyString; + + // String get _userId => GetStorage().read(keyUserId) ?? emptyString; + + // Map get headers => {HttpHeaders.authorizationHeader: _token}; + + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + // if (_token.isNotEmpty) { + // options.headers.addAll({HttpHeaders.authorizationHeader: _token}); + // } + + if (kDebugMode) { + // Logger.print(options); + _CurlLogger.print(options); + } + + return super.onRequest(options, handler); + } + + @override + void onResponse(Response response, ResponseInterceptorHandler handler) { + if (kDebugMode) { + log('RESPONSE(${response.statusCode}) => ${response.requestOptions.baseUrl + response.requestOptions.path}' + '\n' + "api response: ${jsonEncode(response.data)}"); + } + return super.onResponse(response, handler); + } + + @override + void onError(DioException err, ErrorInterceptorHandler handler) { + if (kDebugMode) { + debugPrint( + 'ERROR(${err.response?.statusCode}) => ${err.requestOptions.baseUrl + err.requestOptions.path}', + ); + } + return super.onError(err, handler); + } +} + +abstract class _SimpleLogger { + static print(RequestOptions options) { + debugPrint('REQUEST(${options.method}) => ${options.uri.toString()}'); + debugPrint('Headers: ${options.headers}'); + if (options.queryParameters.isNotEmpty) { + log("queryParameters: ${options.queryParameters}"); + } + + if (options.data != null) { + if (options.data is FormData) { + debugPrint("data: "); + for (var value in (options.data as FormData).fields) { + log(value.toString()); + } + + debugPrint("files: "); + for (var value in (options.data as FormData).files) { + log("${value.key}: ${value.value.filename}"); + } + } else { + log("data: ${options.data}"); + } + } + } +} + +abstract class _CurlLogger { + static print(RequestOptions options) { + String curl = 'curl'; + curl += " --location '${options.uri}'"; + if (options.data != null) { + // Add the data + + if (options.data is FormData) { + for (MapEntry entry in (options.data as FormData).fields) { + curl += " --form '${entry.key}=\"${entry.value}\"'"; + } + + for (MapEntry entry + in (options.data as FormData).files) { + curl += " --form '${entry.key}=@\"${entry.value.filename}\"'"; + } + } else { + curl += ' --data \'${options.data}\''; + } + } + + if (options.headers.isNotEmpty) { + // Add the headers + options.headers.forEach((key, value) { + curl += ' --header \'$key: $value\''; + }); + } + log(curl); + } +} diff --git a/lib/web_services/message_services.dart b/lib/web_services/message_services.dart new file mode 100644 index 0000000..e15e64e --- /dev/null +++ b/lib/web_services/message_services.dart @@ -0,0 +1,14 @@ +import 'http_request_client.dart'; + +class MessageService { + static final MessageService _instance = MessageService._private(); + + MessageService._private(); + + factory MessageService() { + return _instance; + } + + final HttpRequestClient _httpClient = HttpRequestClient(); +// final SessionManagement _sessionManagement = SessionManagement(); +} diff --git a/lib/web_services/notification_services.dart b/lib/web_services/notification_services.dart new file mode 100644 index 0000000..bb71fd2 --- /dev/null +++ b/lib/web_services/notification_services.dart @@ -0,0 +1,45 @@ +import 'http_request_client.dart'; +import 'web_url.dart'; + +class NotificationService { + static final NotificationService _instance = NotificationService._private(); + + NotificationService._private(); + + factory NotificationService() { + return _instance; + } + + final HttpRequestClient _httpClient = HttpRequestClient(); + +// final SessionManagement _sessionManagement = SessionManagement(); + + Future getNotifications() async { + final responseModel = await _httpClient.customRequest( + "GET", + url: WebUrls.getNotifications, + requestHeader: {'Content-Type': 'application/json'}, + isBearerHeaderRequired: true, + isBearer: true, + ); + + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + // if (responseModel.data is List && + // (responseModel.data as List).isNotNullOrEmpty()) { + // return GetRiskAssessmentResponse( + // success: true, + // data: (responseModel.data as List) + // .map((e) => RiskAssessmentData.fromJson(e)) + // .toList()); + // } + // + // return GetRiskAssessmentResponse(success: true, data: []); + + return []; + } else { + return responseModel.statusDescription; + // return GetRiskAssessmentResponse( + // success: false, message: responseModel.statusDescription); + } + } +} diff --git a/lib/web_services/rota_services.dart b/lib/web_services/rota_services.dart new file mode 100644 index 0000000..1ab2a7d --- /dev/null +++ b/lib/web_services/rota_services.dart @@ -0,0 +1,178 @@ +import 'dart:convert'; + +import 'package:ftc_mobile_app/ftc_mobile_app.dart'; + +import '../models/requests/HolidayRequestData.dart'; +import '../models/rota/LiveRoasterResponseData.dart'; +import '../models/staffWorkload/StaffWorkloadResponse.dart'; + +class RotaService { + static final RotaService _instance = RotaService._private(); + + RotaService._private(); + + factory RotaService() { + return _instance; + } + + final HttpRequestClient _httpClient = HttpRequestClient(); + + Future getMyShifts( + {required int startDateMills, required int endDateMills}) async { + // final userJson = LocalStorageManager.getSessionToken( + // tokenKey: LocalStorageKeys.kUserModelKey, + // ); + // final userModel = UserModel.fromJson(jsonDecode(userJson)); + + Map requestBody = { + "startDate": startDateMills, + "endDate": endDateMills, + "staffUserId": LocalStorageManager.userId, + // "startDate": 1720983600000, + // "endDate": 1721502000000, + // "staffUserId": "659653a31faf0d9fa4e15d5d", + }; + ResponseModel responseModel = await _httpClient.customRequest('POST', + requestBody: requestBody, + url: WebUrls.myShifts, + requestHeader: {'Content-Type': 'application/json'}, + isBearer: true, + isBearerHeaderRequired: true); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + return LiveRoasterResponseData.fromJson({'data': responseModel.data}); + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future getAvailableShifts() async { + Map requestBody = { + "startDate": DateTime.now().millisecondsSinceEpoch + }; + ResponseModel responseModel = await _httpClient.customRequest('POST', + requestBody: requestBody, + url: WebUrls.unassignedShifts, + requestHeader: {'Content-Type': 'application/json'}, + isBearer: true, + isBearerHeaderRequired: true); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + return LiveRoasterResponseData.fromJson({'data': responseModel.data}); + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future claimShift({ + required String rosterId, + required String locationId, + required String dayId, + }) async { + // final userJson = LocalStorageManager.getSessionToken( + // tokenKey: LocalStorageKeys.kUserModelKey, + // ); + // final userModel = UserModel.fromJson(jsonDecode(userJson)); + Map requestBody = { + "rosterId": rosterId, + "locationId": locationId, + "staffUserId": LocalStorageManager.userId, + "dayId": dayId, + }; + ResponseModel responseModel = await _httpClient.customRequest('POST', + requestBody: requestBody, + url: WebUrls.assignStaffToShift, + requestHeader: {'Content-Type': 'application/json'}, + isBearer: true, + isBearerHeaderRequired: true); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + print("responseModel.data type: ${responseModel.data.runtimeType}"); + return LiveRoasterResponseData.fromJson({'data': responseModel.data}); + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future serviceUserShifts( + {required String serviceUserId, + required int month, + required int year}) async { + Map requestBody = { + "serviceUser": serviceUserId, + "month": month, //Dec + "year": year, + }; + ResponseModel responseModel = await _httpClient.customRequest('POST', + requestBody: requestBody, + url: WebUrls.rotaServiceUserShiftsListUrl, + requestHeader: {'Content-Type': 'application/json'}, + isBearer: true, + isBearerHeaderRequired: true); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + return (responseModel.data['filteredShifts'] is List) && + responseModel.data['filteredShifts'].isNotEmpty + ? MonthWiseRecord.fromJson(responseModel.data['filteredShifts'][0]) + : false; + } else { + return { + "message": responseModel.statusDescription, + }; + } + } + + Future getStaffWorkload() async { + // final userJson = LocalStorageManager.getSessionToken( + // tokenKey: LocalStorageKeys.kUserModelKey, + // ); + // print("userJson: $userJson"); + // final userModel = UserModel.fromJson(jsonDecode(userJson)); + + Map requestBody = { + "sortproperty": "createdAt", + "sortorder": -1, + "offset": 0, + "limit": 1, + "query": { + "critarion": {"staffMember": LocalStorageManager.userId}, + "staffMemberfields": "staffMemberName", + "usersFields": "profile_picture_url name", + "addedby": "_id email name", + "lastModifiedBy": "_id email name" + } + }; + ResponseModel responseModel = await _httpClient.customRequest('POST', + requestBody: requestBody, + url: WebUrls.getStaffWorkload, + requestHeader: {'Content-Type': 'application/json'}, + isBearer: true, + isBearerHeaderRequired: true); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + return StaffWorkloadResponse.fromJson({'data': responseModel.data}) + ..status = "Success" + ..message = responseModel.statusDescription; + } else { + return responseModel.statusDescription; + } + } + + Future requestHoliday({required HolidayRequestData request}) async { + Map requestBody = + (request..staffRequester = LocalStorageManager.userId).toJson(); + + final ResponseModel responseModel = await _httpClient.customRequest('POST', + requestBody: requestBody, + url: WebUrls.requestHoliday, + requestHeader: {'Content-Type': 'application/json'}, + isBearer: true, + isBearerHeaderRequired: true); + if (responseModel.statusCode >= 200 && responseModel.statusCode <= 230) { + return responseModel; + } else { + return responseModel.statusDescription; + } + } +} diff --git a/lib/web_services/web_url.dart b/lib/web_services/web_url.dart new file mode 100644 index 0000000..3b1725d --- /dev/null +++ b/lib/web_services/web_url.dart @@ -0,0 +1,105 @@ +class WebUrls extends _BaseUrl { + WebUrls._(); + + static String baseUrl = _BaseUrl.baseUrl; //Test + // static String baseUrl = _BaseUrl.liveBaseUrl; //Live + + static String socketUrl = 'http://16.171.242.62:3000'; + + static String signInUrl = "$baseUrl/users/signin"; + static String passwordLessSignInUrl = "$baseUrl/users/passwordLessLogin"; + static String forgetPasswordUrl = "$baseUrl/users/forgot-password"; + static String verifyCodeUrl = "$baseUrl/users/verify-code"; + static String verifyTokenUrl = "$baseUrl/users/verifyToken"; + static String rotaServiceUserShiftsListUrl = + "$baseUrl/serviceUserShifts/getSrUsShiftsRequiredsListForAMonth"; + static String userProfileUrl = + "$baseUrl/staffMembers/getStaffMembersWithFullDetails"; + static String addConsentUrl = + "$baseUrl/consentTemplateRoute/addConsentTemplate"; + static String getConsentListUrl = + "$baseUrl/consentTemplateRoute/getconsentTemplate"; + static String deleteConsentUrl = + "$baseUrl/consentTemplateRoute/removeConsentTemplate"; + static String updateConsentUrl = + "$baseUrl/consentTemplateRoute/updateConsentTemplate"; + + // static String getServiceUsersListUrl = + // "${baseUrl}/serviceUsers/getServiceUsersWithFullDetails"; + static String getServiceUsersListUrl = "$baseUrl/users/listAllServiceUsers"; + static String getBodyPointsCategoryListURL = + "$baseUrl/consentTemplateRoute/getHealthCategories"; + static String addHealthIssuesURL = + "$baseUrl/consentTemplateRoute/addHealthIssues"; + static String getHealthIssuesListURL = + "$baseUrl/consentTemplateRoute/getHealthIssues"; + static String updateHealthIssueURL = + "$baseUrl/consentTemplateRoute/updateHealthIssues"; + static String getHealthIssueChildCategoryListURL = + "$baseUrl/consentTemplateRoute/getChildHealthCategories"; + static String addRecentIncidentServiceURL = "$baseUrl/incident/addIncident"; + static String getRecentIncidentsListServiceURL = + "$baseUrl/incident/getIncidentWithFullDetails"; + static String updateRecentIncidentServiceURL = + "$baseUrl/incident/updateIncident"; + static String deleteRecentIncidentUrl = "$baseUrl/incident/removeIncident"; + static String addPbsPlanServiceURL = "$baseUrl/pbsplans/addPbsplan"; + static String updatePbsPlanServiceURL = "$baseUrl/pbsplans/updatePbsplan"; + static String getPbsPlanServiceURL = "$baseUrl/pbsplans/getPbsPlanList"; + static String deletePbsPlanServiceURL = "$baseUrl/pbsplans/deletePbsPlanList"; + static String getDocumentsListServiceURL = "$baseUrl/document/documentList"; + static String addDocumentServiceURL = "$baseUrl/document/addDocument"; + static String updateDocumentServiceURL = "$baseUrl/document/updateDocument"; + static String deleteDocServiceURL = "$baseUrl/document/removeDocument"; + static String uploadDocServiceURL = "$baseUrl/uploads/uploadReferenceDoc"; + static String addSingleMessageChatURL = "$baseUrl/messages/addMessage"; + static String uploadMessageAttachmentsURL = + "$baseUrl/messages/uploadMessageAttachments"; + static String addGroupMessageServiceURL = "$baseUrl/messages/addGroupMessage"; + static String deleteGroupMessageServiceURL = + "$baseUrl/messages/deleteGroupMessage"; + static String updateGroupMessageServiceURL = + "$baseUrl/messages/updateGroupMessage"; + static String combinedLastMessageURL = + "$baseUrl/messages/combinedLastMessages"; + static String allGroupMessagesURL = "$baseUrl/messages/messageByGroupId"; + static String allSingleChatMessagesURL = "$baseUrl/messages/getAllMessage"; + static String allSingleChatMessagesServiceAdminURL = + "$baseUrl/messages/getMessage"; + static String updateSingleChatMessageURL = "$baseUrl/messages/updateMessage"; + static String deleteSingleChatMessageURL = "$baseUrl/messages/deleteMessage"; + + static String deleteChatURL = "$baseUrl/messages/deleteChat"; + + static String createCarePlanURL = "$baseUrl/carePlans/createCarePlan"; + static String getCarePlansListURL = "$baseUrl/carePlans/getCarePlansList"; + static String getStaffWorkload = + "$baseUrl/staffWorkLoads/getStaffWorkLoadsWithFullDetails"; + static String requestHoliday = + "$baseUrl/staffHolidayRequests/createStaffHolidayRequest"; + static String myShifts = "$baseUrl/liveRoster/checkExistShiftByStaffId"; + static String unassignedShifts = "$baseUrl/liveRoster/unassignedShifts"; + static String assignStaffToShift = "$baseUrl/liveRoster/assignStaffToShift"; + + static String getAppointmentsByDate = + "$baseUrl/appointment/getAppointmentsByDate"; + + static String getRiskAssesments = "$baseUrl/riskassesments/getRiskAssesments"; + + static String getNotifications = "$baseUrl/notifications/getNotifications"; + + static String getMemoryList = "$baseUrl/carePlans/getMemoryList"; + static String uploadMemoryBoxFile = "$baseUrl/carePlans/uploadMemoryBoxFile"; + static String addMemoryBox = "$baseUrl/carePlans/addMemoryBox"; + static String updateMemoryBox = "$baseUrl/carePlans/updateMemoryBox"; + static String createRiskAssesments = + "$baseUrl/riskassesments/createRiskAssesments"; + static String listAllUsers = "$baseUrl/users/listAllUsers"; + static String allTrainings = + "$baseUrl/proposedTrainings/getProposedTrainingsByStaff"; +} + +abstract class _BaseUrl { + static const baseUrl = "http://16.171.242.62:3000"; + static const liveBaseUrl = "https://ftcaresoftware.co.uk/ft_care_server"; +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..06d6f04 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,89 @@ +name: ftc_mobile_app +description: A new Flutter project. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +version: 1.0.0+1 + +environment: + sdk: '>=3.0.0 <4.0.0' +dependencies: + flutter: + sdk: flutter + cupertino_icons: ^1.0.2 + http: ^1.2.0 +# http: ^0.13.6 + get: 4.6.6 + get_storage: ^2.1.1 + flutter_screenutil: ^5.9.0 + flutter_svg: ^2.0.9 + pin_code_fields: ^8.0.1 + flutter_calendar_carousel: ^2.4.3 + intl: ^0.18.1 + fluttertoast: ^8.2.4 + flutter_keyboard_visibility: ^5.4.1 + quill_html_editor: ^2.1.9 +# awesome_dialog: ^3.2.0 + file_picker: ^6.2.1 +# file_picker: ^8.0.1 + path_provider: ^2.1.3 + socket_io_client: ^2.0.3+1 + adoptive_calendar: ^0.1.5 + flutter_html: ^3.0.0-beta.2 + webview_flutter: ^4.8.0 + pull_to_refresh_flutter3: ^2.0.2 + cached_network_image: ^3.3.1 + get_time_ago: ^1.3.1 + grouped_list: ^5.1.2 + dio: ^5.4.3+1 + video_thumbnail: ^0.5.3 + video_player: ^2.9.0 + firebase_messaging: ^14.7.18 + flutter_local_notifications: ^16.3.3 + firebase_core: ^2.26.0 + flutter_app_badger: ^1.5.0 + stop_watch_timer: ^3.1.1 + photo_view: ^0.14.0 + url_launcher: ^6.3.0 + image_picker: ^1.1.2 + permission_handler: ^11.3.1 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + flutter_launcher_icons: ^0.13.1 + flutter_native_splash: ^2.2.11 + +# For generating Splash Screen +flutter_native_splash: + color: "#278DEC" + + android_12: + color: "#278DEC" + +flutter: + uses-material-design: true + assets: + - assets/fonts/ + - assets/images/png/ + - assets/images/svg/ + - assets/images/svg/careNotesCategories/ + - assets/images/svg/careNotesSubcatgeories/ + - assets/images/png/ratings/ + - assets/consent-capacity.html + + fonts: + - family: Roboto + fonts: + - asset: assets/fonts/Roboto-Black.ttf + - asset: assets/fonts/Roboto-BlackItalic.ttf + - asset: assets/fonts/Roboto-Bold.ttf + - asset: assets/fonts/Roboto-BoldItalic.ttf + - asset: assets/fonts/Roboto-Italic.ttf + - asset: assets/fonts/Roboto-Light.ttf + - asset: assets/fonts/Roboto-LightItalic.ttf + - asset: assets/fonts/Roboto-Medium.ttf + - asset: assets/fonts/Roboto-MediumItalic.ttf + - asset: assets/fonts/Roboto-Regular.ttf + - asset: assets/fonts/Roboto-Thin.ttf + - asset: assets/fonts/Roboto-ThinItalic.ttf \ No newline at end of file