diff --git a/.firebase/hosting.YnVpbGQvd2Vi.cache b/.firebase/hosting.YnVpbGQvd2Vi.cache
deleted file mode 100644
index 9c0a10e1856acb75a4f3601591cdb87449c7834d..0000000000000000000000000000000000000000
--- a/.firebase/hosting.YnVpbGQvd2Vi.cache
+++ /dev/null
@@ -1,14 +0,0 @@
-favicon.png,1584779381428,fcc7c4545d5b62ad01682589e6fdc7ea03d0a3b42069963c815c344b632eb5cf
-flutter_service_worker.js,1584901267607,8f801cedfa47876022973baf2c58466ca901503e035e0ecc23be15a326912675
-manifest.json,1584783582956,ec5652f2a192fb913800fb92fafcd77941e2d9c4fade39fe6c7abac9ed9e3170
-assets/AssetManifest.json,1584901267102,3483c0e5487d0a84a9d3ed20281d4ba7800154bc6194d426987fb0187fc24a31
-assets/FontManifest.json,1584901267102,9cd4a4d214a9fce72dd36c28e1ef4a60895d922bc0e25613a2539a6fdd1a3921
-index.html,1584893603570,ccae05411f7685dde888dba2a5c76d0e7f6e9fee83f6c2febf4734eac8d0287f
-icons/Icon-192.png,1584779381428,d2e0131bb7851eb9d98f7885edb5ae4b4d6b7a6c7addf8a25b9b712b39274c0f
-icons/Icon-512.png,1584779381428,7a31ce91e554f1941158ca46f31c7f3f2b7c8c129229ea74a8fae1affe335033
-assets/assets/images/google_logo.png,1584889755884,617c12e1e5d33f572e0e819bacad57d66b0e8292fd07aa04fe7c21746852ffb9
-assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1575319380000,3d5615fc7a84c016d70e93141c13a613fda9babcede78918fbd190f0b9d5b578
-assets/fonts/MaterialIcons-Regular.ttf,1475273174000,49f9a5d2c31ecd331d178c6c3d587181ce635721e46240a8547f0a5374f3257d
-assets/LICENSE,1584901267102,83612489337a65c2e762255b27a9298c3c3940c6b67b2af5ebe0374643ab2d69
-main.dart.js,1584901264058,3bf3fedd30adf74c89a82ba23ec682abd2e30b49dd0a7ea0283f189d5513b76e
-main.dart.js.map,1584901264389,5a03322124ee86bbff8700cb6b220a4aea0a40db520d7be4b0e0ec4e99f559d8
diff --git a/.firebaserc b/.firebaserc
index 593ffa971915557566a034ce0c99257f294d5203..29400fde069967fff8980018c7ad06ae19c3d7de 100644
--- a/.firebaserc
+++ b/.firebaserc
@@ -1,5 +1,6 @@
 {
   "projects": {
-    "default": "corona-fasilkom"
+    "default": "waspadabencana-staging",
+    "prod": "waspadabencana-prod"
   }
-}
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 8003b58cd074870cc7ac397de9afa2f446f33c3a..cdd865b98ce5ef41ca092b5b97fed5e9f7013bda 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
 *.log
 *.pyc
 *.swp
+coverage/
 .DS_Store
 .atom/
 .buildlog/
@@ -74,3 +75,9 @@
 .flutter-plugins-dependencies
 
 lib/generated_plugin_registrant.dart
+
+# Firebase build folder
+.firebase/
+
+#Configuration file not need to be included
+android/key.properties
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5eb8514fd7183d11b18f23e611fcd4b53dcb8947..dc8e36e79e63dc4cdacf5e956df3f8bec6c19bbf 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -11,7 +11,6 @@ lint:
     - flutter analyze
 
 test:
-  stage: test
   image: cirrusci/flutter:v1.15.3-web
   stage: test
   coverage: '/lines......: \d+\.\d+\%/'
@@ -32,14 +31,121 @@ build-web:
   artifacts:
     paths:
       - build/web
+  only:
+    - staging
+    - master
+
+deploy-web-staging:
+  stage: deploy
+  image: andreysenov/firebase-tools:latest
+  script:
+    # This line enables using experimental Skia rendering engine to massively increase Flutter Web performance!
+    - FLUTTER_WEB_USE_SKIA=true
+    - firebase use $PROJECT_STAGING_NAME --token $FIREBASE_TOKEN_STAGING
+    - firebase deploy --only hosting -m "Pipe $CI_PIPELINE_ID Build $CI_BUILD_ID" --token $FIREBASE_TOKEN_STAGING
+  only:
+    - staging
 
-deploy:
+deploy-web-master:
   stage: deploy
   image: andreysenov/firebase-tools:latest
   script:
     # This line enables using experimental Skia rendering engine to massively increase Flutter Web performance!
-    # Remove this line if encounter any issue with rendering.
     - FLUTTER_WEB_USE_SKIA=true
-    - TOKEN="1//0g6T_ox804J8zCgYIARAAGBASNwF-L9IruWNUi840C2WIKLKyZ5zc5ZveCpsbkCeXOeI8YAc_Gly1HynegIbXzXvUCGjiwI2NsVw"
-    - firebase use corona-fasilkom --token $TOKEN
-    - firebase deploy --only hosting -m "Pipe $CI_PIPELINE_ID Build $CI_BUILD_ID" --token $TOKEN
+    - firebase use $PROJECT_PROD_NAME --token $FIREBASE_TOKEN_PROD
+    - firebase deploy --only hosting -m "Pipe $CI_PIPELINE_ID Build $CI_BUILD_ID" --token $FIREBASE_TOKEN_PROD
+  only:
+    - master
+
+build-apk-staging:
+  stage: build
+  image: hafiyyan94/flutter-beta:beta
+  script:
+    - flutter build apk --debug --obfuscate --split-debug-info=./logs
+    - cd build/app/outputs/apk/debug/
+    - cp app-debug.apk sigap-app.apk
+  artifacts:
+    paths:
+      - build/app/outputs/apk/debug/sigap-app.apk
+    expire_in: 1 days
+  only:
+    - staging
+
+build-apk-prod:
+  stage: build
+  image: hafiyyan94/flutter-beta:beta
+  before_script:
+    - rm android/app/google-services.json
+    - echo $GSERVICE_JSON_PROD | base64 -d > android/app/google-services.json
+    - echo $KEY_PROPERTIES | base64 -d > android/key.properties
+  script:
+    - flutter build apk --release --obfuscate --split-debug-info=./logs
+    - cd build/app/outputs/apk/release/
+    - cp app-release.apk sigap-app.apk
+  artifacts:
+    paths:
+      - build/app/outputs/apk/release/sigap-app.apk
+    expire_in: 1 days
+  only:
+    - master
+
+build-apk-release:
+  stage: build
+  image: hafiyyan94/flutter-beta:beta
+  before_script:
+    - rm android/app/google-services.json
+    - echo $GSERVICE_JSON_PROD | base64 -d > android/app/google-services.json
+    - echo $KEY_PROPERTIES | base64 -d > android/key.properties
+  script:
+    - flutter build appbundle --release --obfuscate --split-debug-info=./logs
+    - cp build/app/outputs/bundle/release/app-release.aab sigap-release.aab
+  artifacts:
+    paths:
+      - sigap-release.aab
+    expire_in: 1 days
+  only:
+    - /^release-.*$/
+
+build-plist-prod:
+  stage: build
+  image: python:latest
+  script:
+    - echo $GSERVICE_PLIST_PROD | base64 -d > ios/Runner/GoogleService-Info.plist
+  artifacts:
+    paths:
+      - GoogleService-Info.plist
+    expire_in: 1 days
+  only:
+    - /^release-.*$/
+
+build-plist-release:
+  stage: build
+  image: python:latest
+  script:
+    - echo $GSERVICE_PLIST_RELEASE | base64 -d > ios/Runner/GoogleService-Info.plist
+  artifacts:
+    paths:
+      - GoogleService-Info.plist
+    expire_in: 1 days
+  only:
+    - /^release-.*$/
+
+build-web-release:
+  stage: deploy
+  image: cirrusci/flutter:v1.15.3-web
+  before_script:
+    - rm web/index.html
+    - echo $PROD_INDEX_HTML | base64 -d > web/index.html
+  script:
+    - flutter config --enable-web
+    - flutter build web --release
+    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
+    - eval $(ssh-agent -s)
+    - ssh-add <(echo "$prod_pem")
+    - mkdir -p ~/.ssh
+    - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
+    - ssh-keyscan -t rsa "$prod_ip" >> ~/.ssh/known_hosts
+    - scp -r "$username_prod@$prod_ip":~/web build/web
+    - ssh "$username_prod@$prod_ip" 'sudo bash /var/www/sites/siaga-wabah/deployment.sh'
+  only:
+    - /^release-.*$/
\ No newline at end of file
diff --git a/CODEOWNER b/CODEOWNER
new file mode 100644
index 0000000000000000000000000000000000000000..59157e85eef1287cc5e0e648444548638470a725
--- /dev/null
+++ b/CODEOWNER
@@ -0,0 +1,3 @@
+.gitlab-ci.yml @hafiyyan94
+LICENSE @hafiyyan94
+README.md @Hafiyyan94
\ No newline at end of file
diff --git a/README.md b/README.md
index 979848830244a00a837154130d068846a6ae3631..cefe6abdce747698604b0cda06a1d86c569b58c2 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,17 @@
 # Fasilkom COVID-19 Project
 A health care equipment marketplace to help COVID-19 situation.
 
+## Table of Contents
+
+1. Getting Started
+2. Using Text Editor or IDE
+3. Setting up different Firebase App
+4. Style Guide
+5. Generate JSON Model (Serializer/Deserializer) Class using pub runner
+6. Architecture/State Management Guide
+7. Git Flow
+8. Database Usage
+
 ## Getting Started
 1. Install Flutter by following instructions in [Flutter.dev documentation](https://flutter.dev/docs/get-started/install)
 2. Add flutter to your $PATH if you have not done so
@@ -16,7 +27,9 @@ flutter config --enable-web
 ```bash
 flutter run -d chrome
 ```
-6. Enjoy the benefit of Flutter development! You can do hot reload by pressing r (small r) in the terminal and hot restart by pressing R (capital r), but if you 
+> Note: if you're using Chromium, you may need to set `CHROME_EXECUTABLE` to point to your Chromium executable.
+
+6. Enjoy the benefit of Flutter development! You can do hot reload by pressing r (small r) in the terminal and hot restart by pressing R (capital r), but if you
 
 ## Using Text Editor or IDE
 For Flutter development, you can both use VSCode or IntelliJ Idea.
@@ -28,7 +41,9 @@ In IntelliJ, there will be Dropdown menu beside the start button.
 For using different Firebase App than the one that is used in this project do:
 - Android: Change `android/app/google-services.json` with your own `google-services.json` file
 - Web: Change `firebaseConfig` variables in `web/index.html` with your own config from your Firebase console
-- iOS: TODO
+- iOS: Change `ios/Runner/GoogleService-Info.plist` with your own `GoogleService-Info.plist` file
+
+Currently, all these configurations set for Staging Environment prepared by SigapCS Team
 
 ## Style Guide
 This codebase will use [Effective Dart](https://dart.dev/guides/language/effective-dart) style guide as it's primary source of truth for linting. To achieve strictness of code quality, we use [pedantic](https://pub.dev/packages/pedantic), the package that is used in Google for ensuring code quality and consistency.
@@ -55,7 +70,7 @@ For more info about JSON serialization head over to [this documentation](https:/
 ## Architecture/State Management Guide
 This Flutter app uses a library called [provider](https://pub.dev/packages/provider) for managing it's state.
 Why Provider? Because it is very simple and relatively easier to learn compared to state management patterns like Redux, MobX, etc.
-The idea of Provider is actually very simple: Lift the state of a Widget up to the Provider, and make it accessible to all Widget that are descendants of the Provider widget. 
+The idea of Provider is actually very simple: Lift the state of a Widget up to the Provider, and make it accessible to all Widget that are descendants of the Provider widget.
 According to the author, provider is a mixture between dependency injection and state management, built with widgets for widgets.
 If you think that Provider is not clean enough to manage the state, you can combine it by using BLoC Pattern, this pattern is recommended by the Google Flutter team on the Google I/O event.
 For more tutorial about Provider pattern, please check out [this link](https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple).
diff --git a/android/.project b/android/.project
new file mode 100644
index 0000000000000000000000000000000000000000..3964dd3f5b7fea7ecf391254ba292fcfe96bb513
--- /dev/null
+++ b/android/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>android</name>
+	<comment>Project android created by Buildship.</comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
+	</natures>
+</projectDescription>
diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..e8895216fd3c0c3af4c4522334775f41b7deb42e
--- /dev/null
+++ b/android/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,2 @@
+connection.project.dir=
+eclipse.preferences.version=1
diff --git a/android/app/.classpath b/android/app/.classpath
new file mode 100644
index 0000000000000000000000000000000000000000..eb19361b5711e614b090eac2f1b51309da2ec1e4
--- /dev/null
+++ b/android/app/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
+	<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
+	<classpathentry kind="output" path="bin/default"/>
+</classpath>
diff --git a/android/app/.project b/android/app/.project
new file mode 100644
index 0000000000000000000000000000000000000000..ac485d7c3e627d997f59da65ac961160079a0143
--- /dev/null
+++ b/android/app/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>app</name>
+	<comment>Project app created by Buildship.</comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
+	</natures>
+</projectDescription>
diff --git a/android/app/.settings/org.eclipse.buildship.core.prefs b/android/app/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..b1886adb46c085de842f1283c1a3c25151bfc988
--- /dev/null
+++ b/android/app/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,2 @@
+connection.project.dir=..
+eclipse.preferences.version=1
diff --git a/android/app/build.gradle b/android/app/build.gradle
index f39456d4b3597adad1eca318167855e4d8610a79..e293ff5c5a6fba8193fe13cf7eaeea1d142d4717 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -28,6 +28,12 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
 // Apply plugin for Firebase
 apply plugin: 'com.google.gms.google-services'
 
+def keystoreProperties = new Properties()
+def keystorePropertiesFile = rootProject.file('key.properties')
+if (keystorePropertiesFile.exists()) {
+    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+}
+
 android {
     compileSdkVersion 28
 
@@ -41,7 +47,7 @@ android {
 
     defaultConfig {
         // TODO: Change this application ID to proper ID
-        applicationId "id.ui.cs.coronafasilkom"
+        applicationId "id.ac.ui.cs.sigap"
         minSdkVersion 16
         targetSdkVersion 28
         versionCode flutterVersionCode.toInteger()
@@ -50,6 +56,14 @@ android {
         // This is required to add cloud_firestore plugin
         multiDexEnabled true
     }
+    signingConfigs {
+        release {
+            keyAlias keystoreProperties['keyAlias']
+            keyPassword keystoreProperties['keyPassword']
+            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
+            storePassword keystoreProperties['storePassword']
+        }
+    }
 
     buildTypes {
         release {
@@ -58,7 +72,7 @@ android {
 
             // 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
+            signingConfig signingConfigs.release
         }
     }
 }
@@ -69,4 +83,6 @@ flutter {
 
 dependencies {
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+    implementation 'com.google.firebase:firebase-analytics:17.2.3'
+    implementation 'com.google.firebase:firebase-firestore:21.4.1'
 }
diff --git a/android/app/google-services.json b/android/app/google-services.json
index 853355af00e100a3840a431eb02b68294aac32af..2fa84ae846fb55e23a1c929db83d8b8f4ff41046 100644
--- a/android/app/google-services.json
+++ b/android/app/google-services.json
@@ -1,34 +1,34 @@
 {
   "project_info": {
-    "project_number": "341221363005",
-    "firebase_url": "https://corona-fasilkom.firebaseio.com",
-    "project_id": "corona-fasilkom",
-    "storage_bucket": "corona-fasilkom.appspot.com"
+    "project_number": "717726977502",
+    "firebase_url": "https://waspadabencana-staging.firebaseio.com",
+    "project_id": "waspadabencana-staging",
+    "storage_bucket": "waspadabencana-staging.appspot.com"
   },
   "client": [
     {
       "client_info": {
-        "mobilesdk_app_id": "1:341221363005:android:e6e1521d7317cbc4a7c954",
+        "mobilesdk_app_id": "1:717726977502:android:b30f774dee20de1dba214f",
         "android_client_info": {
-          "package_name": "id.ui.cs.coronafasilkom"
+          "package_name": "id.ac.ui.cs.sigap"
         }
       },
       "oauth_client": [
         {
-          "client_id": "341221363005-ev5ijn7e8vn3qnrgjtmfjci5an4mtjij.apps.googleusercontent.com",
+          "client_id": "717726977502-u9elfk2dro0pdes9id94eqo0kd4fgpg8.apps.googleusercontent.com",
           "client_type": 3
         }
       ],
       "api_key": [
         {
-          "current_key": "AIzaSyCxcEQkWjDlolAzS9dFvq9viW8ghRlOcpQ"
+          "current_key": "AIzaSyDIiWLNXE9BybKr4HiIIgaUUTlu5ANSGDM"
         }
       ],
       "services": {
         "appinvite_service": {
           "other_platform_oauth_client": [
             {
-              "client_id": "341221363005-ev5ijn7e8vn3qnrgjtmfjci5an4mtjij.apps.googleusercontent.com",
+              "client_id": "717726977502-u9elfk2dro0pdes9id94eqo0kd4fgpg8.apps.googleusercontent.com",
               "client_type": 3
             }
           ]
@@ -37,4 +37,4 @@
     }
   ],
   "configuration_version": "1"
-}
\ No newline at end of file
+}
diff --git a/android/app/key.jks b/android/app/key.jks
new file mode 100644
index 0000000000000000000000000000000000000000..cc520735cf50160500c83d75d1d8d63458dd99ff
Binary files /dev/null and b/android/app/key.jks differ
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 36de62181fad574ffa1a22c8133cb3bf69083d5d..d23341902d40a374cbc09d07d2aa34dc189c5a7d 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -7,8 +7,8 @@
          FlutterApplication and put your custom class here. -->
     <application
         android:name="io.flutter.app.FlutterApplication"
-        android:label="mobile_apps"
-        android:icon="@mipmap/ic_launcher">
+        android:label="Sigap"
+        android:icon="@mipmap/launcher_icon">
         <activity
             android:name=".MainActivity"
             android:launchMode="singleTop"
diff --git a/android/app/src/main/kotlin/com/example/mobile_apps/MainActivity.kt b/android/app/src/main/kotlin/com/example/mobile_apps/MainActivity.kt
index 5675817bdc09d4c3c1ed4fef4450379ff861851c..b791f5ca02e1bb1aef6805cbda57fc4ee3f25cba 100644
--- a/android/app/src/main/kotlin/com/example/mobile_apps/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/example/mobile_apps/MainActivity.kt
@@ -2,5 +2,8 @@ package com.example.mobile_apps
 
 import io.flutter.embedding.android.FlutterActivity
 
+import android.os.Build
+import android.view.ViewTreeObserver
+import android.view.WindowManager
 class MainActivity: FlutterActivity() {
-}
+}
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable-hdpi/splash.png b/android/app/src/main/res/drawable-hdpi/splash.png
new file mode 100644
index 0000000000000000000000000000000000000000..2e676e4cdbec21219a7c53cb1147b07a68cd01db
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/splash.png b/android/app/src/main/res/drawable-mdpi/splash.png
new file mode 100644
index 0000000000000000000000000000000000000000..1f479771d583acc37d21530367c4523fd352ea15
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/splash.png b/android/app/src/main/res/drawable-xhdpi/splash.png
new file mode 100644
index 0000000000000000000000000000000000000000..a681f6661d7cc4416f6157f45cbea9440db7be51
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/splash.png b/android/app/src/main/res/drawable-xxhdpi/splash.png
new file mode 100644
index 0000000000000000000000000000000000000000..0e77b361e7821291946f48ce68c03cfd95c89435
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/splash.png b/android/app/src/main/res/drawable-xxxhdpi/splash.png
new file mode 100644
index 0000000000000000000000000000000000000000..a54282aa578f1876e716a1fc18e17b326803ad56
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml
index 304732f8842013497e14bd02f67a55f2614fb8f7..8c09435ea9ac7cf8490ce68073345509f551a26b 100644
--- a/android/app/src/main/res/drawable/launch_background.xml
+++ b/android/app/src/main/res/drawable/launch_background.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- Modify this file to customize your launch splash screen -->
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/white" />
+    <item android:drawable="@color/splash_color" />
 
     <!-- You can insert your own image assets here -->
     <!-- <item>
@@ -9,4 +9,8 @@
             android:gravity="center"
             android:src="@mipmap/launch_image" />
     </item> -->
+    <item>
+        <bitmap android:gravity="center" android:src="@drawable/splash" />
+    </item>
+
 </layer-list>
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 0000000000000000000000000000000000000000..1d96626de9c8e9148fca2153d1c9275e80111085
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/launcher_icon.png b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..9cda7266296fcf06b8ee7d647831da95459ef5a8
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/launcher_icon.png b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..18e93d88c4d843bd8aef248d059ca08084003ce3
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/launcher_icon.png b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..e2da3978ac1865498f9bddc3087e92cd32a1f2ad
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/launcher_icon.png b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..8dd7047f9389792f738bf1403f11bd40491be1dc
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/colors.xml b/android/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f577ec0522416500277c9583ca75897549cbea03
--- /dev/null
+++ b/android/app/src/main/res/values/colors.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="splash_color">#fefefe</color>
+</resources>
\ No newline at end of file
diff --git a/assets/icons/icon-merah.png b/assets/icons/icon-merah.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f6ce6c5b3780a269f47bec7d8c65e38bcec3372
Binary files /dev/null and b/assets/icons/icon-merah.png differ
diff --git a/assets/images/medical-item-placeholder.png b/assets/images/medical-item-placeholder.png
new file mode 100644
index 0000000000000000000000000000000000000000..bef337c609d04e7fe96ac876024a4ed9f9642596
Binary files /dev/null and b/assets/images/medical-item-placeholder.png differ
diff --git a/assets/images/sigap.png b/assets/images/sigap.png
new file mode 100644
index 0000000000000000000000000000000000000000..f2bc83024c4a791015c2ee7268980efb97a9a861
Binary files /dev/null and b/assets/images/sigap.png differ
diff --git a/codemagic.yaml b/codemagic.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a764b6d5a7c6b37a22eb968208f57b5a65c096c6
--- /dev/null
+++ b/codemagic.yaml
@@ -0,0 +1,147 @@
+# Automatically generated on 2020-03-28 UTC from https://codemagic.io/app/5e7ccb63c986420018c1367d/settings
+# Note that this configuration is not an exact match to UI settings. Review and adjust as necessary.
+
+workflows:
+  master-workflow:
+    name: Master Workflow
+    environment:
+      vars:
+        GSERVICE_JSON_PROD: ewogICJwcm9qZWN0X2luZm8iOiB7CiAgICAicHJvamVjdF9udW1iZXIiOiAiMzU3NTY0NjUyOTM2IiwKICAgICJmaXJlYmFzZV91cmwiOiAiaHR0cHM6Ly93YXNwYWRhYmVuY2FuYS1wcm9kLmZpcmViYXNlaW8uY29tIiwKICAgICJwcm9qZWN0X2lkIjogIndhc3BhZGFiZW5jYW5hLXByb2QiLAogICAgInN0b3JhZ2VfYnVja2V0IjogIndhc3BhZGFiZW5jYW5hLXByb2QuYXBwc3BvdC5jb20iCiAgfSwKICAiY2xpZW50IjogWwogICAgewogICAgICAiY2xpZW50X2luZm8iOiB7CiAgICAgICAgIm1vYmlsZXNka19hcHBfaWQiOiAiMTozNTc1NjQ2NTI5MzY6YW5kcm9pZDo4YjNmNWM1ZWVhYWE1MDJmYThhZTNkIiwKICAgICAgICAiYW5kcm9pZF9jbGllbnRfaW5mbyI6IHsKICAgICAgICAgICJwYWNrYWdlX25hbWUiOiAiaWQuYWMudWkuY3Muc2lnYXAiCiAgICAgICAgfQogICAgICB9LAogICAgICAib2F1dGhfY2xpZW50IjogWwogICAgICAgIHsKICAgICAgICAgICJjbGllbnRfaWQiOiAiMzU3NTY0NjUyOTM2LW8zYTBpdHBlNzYxZjgzbTN0Y2hvOGlldmt2dTdvNHI5LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwKICAgICAgICAgICJjbGllbnRfdHlwZSI6IDMKICAgICAgICB9CiAgICAgIF0sCiAgICAgICJhcGlfa2V5IjogWwogICAgICAgIHsKICAgICAgICAgICJjdXJyZW50X2tleSI6ICJBSXphU3lBRE9qaHNSWURZREdOWVpJU0pTLTVIamtIRHI0S0hJMVEiCiAgICAgICAgfQogICAgICBdLAogICAgICAic2VydmljZXMiOiB7CiAgICAgICAgImFwcGludml0ZV9zZXJ2aWNlIjogewogICAgICAgICAgIm90aGVyX3BsYXRmb3JtX29hdXRoX2NsaWVudCI6IFsKICAgICAgICAgICAgewogICAgICAgICAgICAgICJjbGllbnRfaWQiOiAiMzU3NTY0NjUyOTM2LW8zYTBpdHBlNzYxZjgzbTN0Y2hvOGlldmt2dTdvNHI5LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwKICAgICAgICAgICAgICAiY2xpZW50X3R5cGUiOiAzCiAgICAgICAgICAgIH0KICAgICAgICAgIF0KICAgICAgICB9CiAgICAgIH0KICAgIH0KICBdLAogICJjb25maWd1cmF0aW9uX3ZlcnNpb24iOiAiMSIKfQ==
+        GSERVICE_PLIST_PROD: PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+Q0xJRU5UX0lEPC9rZXk+Cgk8c3RyaW5nPjM1NzU2NDY1MjkzNi1yOWY4ZDUyNnFhYW9pbGU5cDNwdDZwODQ0NnNhOG8yOC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbTwvc3RyaW5nPgoJPGtleT5SRVZFUlNFRF9DTElFTlRfSUQ8L2tleT4KCTxzdHJpbmc+Y29tLmdvb2dsZXVzZXJjb250ZW50LmFwcHMuMzU3NTY0NjUyOTM2LXI5ZjhkNTI2cWFhb2lsZTlwM3B0NnA4NDQ2c2E4bzI4PC9zdHJpbmc+Cgk8a2V5PkFQSV9LRVk8L2tleT4KCTxzdHJpbmc+QUl6YVN5QjZSXzV0YXZnOFRoR0t2MEdhRGxGMHlsUEo0cUhuNGlNPC9zdHJpbmc+Cgk8a2V5PkdDTV9TRU5ERVJfSUQ8L2tleT4KCTxzdHJpbmc+MzU3NTY0NjUyOTM2PC9zdHJpbmc+Cgk8a2V5PlBMSVNUX1ZFUlNJT048L2tleT4KCTxzdHJpbmc+MTwvc3RyaW5nPgoJPGtleT5CVU5ETEVfSUQ8L2tleT4KCTxzdHJpbmc+aWQuYWMudWkuY3Muc2lnYXA8L3N0cmluZz4KCTxrZXk+UFJPSkVDVF9JRDwva2V5PgoJPHN0cmluZz53YXNwYWRhYmVuY2FuYS1wcm9kPC9zdHJpbmc+Cgk8a2V5PlNUT1JBR0VfQlVDS0VUPC9rZXk+Cgk8c3RyaW5nPndhc3BhZGFiZW5jYW5hLXByb2QuYXBwc3BvdC5jb208L3N0cmluZz4KCTxrZXk+SVNfQURTX0VOQUJMRUQ8L2tleT4KCTxmYWxzZT48L2ZhbHNlPgoJPGtleT5JU19BTkFMWVRJQ1NfRU5BQkxFRDwva2V5PgoJPGZhbHNlPjwvZmFsc2U+Cgk8a2V5PklTX0FQUElOVklURV9FTkFCTEVEPC9rZXk+Cgk8dHJ1ZT48L3RydWU+Cgk8a2V5PklTX0dDTV9FTkFCTEVEPC9rZXk+Cgk8dHJ1ZT48L3RydWU+Cgk8a2V5PklTX1NJR05JTl9FTkFCTEVEPC9rZXk+Cgk8dHJ1ZT48L3RydWU+Cgk8a2V5PkdPT0dMRV9BUFBfSUQ8L2tleT4KCTxzdHJpbmc+MTozNTc1NjQ2NTI5MzY6aW9zOmU5NDJjZGUzZDdmNzIyMDZhOGFlM2Q8L3N0cmluZz4KCTxrZXk+REFUQUJBU0VfVVJMPC9rZXk+Cgk8c3RyaW5nPmh0dHBzOi8vd2FzcGFkYWJlbmNhbmEtcHJvZC5maXJlYmFzZWlvLmNvbTwvc3RyaW5nPgo8L2RpY3Q+CjwvcGxpc3Q+
+      flutter: beta
+      xcode: latest
+      cocoapods: default
+    triggering:
+      events:
+        - push
+      branch_patterns:
+        - pattern: master
+          include: true
+          source: true
+    scripts:
+      - |
+        # set up debug keystore
+        rm -f ~/.android/debug.keystore
+        keytool -genkeypair \
+          -alias androiddebugkey \
+          -keypass android \
+          -keystore ~/.android/debug.keystore \
+          -storepass android \
+          -dname 'CN=Android Debug,O=Android,C=US' \
+          -keyalg 'RSA' \
+          -keysize 2048 \
+          -validity 10000
+      - |
+        # set up local properties
+        echo "flutter.sdk=$HOME/programs/flutter" > "$FCI_BUILD_DIR/android/local.properties"
+      - flutter packages pub get
+      - flutter config --enable-web
+      - |
+        #!/bin/sh
+
+        ls
+        echo $GSERVICE_PLIST_PROD | base64 -d > ./ios/Runner/GoogleService-Info.plist
+        echo $GSERVICE_JSON_PROD | base64 -d > ./android/app/google-services.json
+        rm ./web/index.html
+        echo $PROD_INDEX_HTML | base64 -d > ./web/index.html
+      - flutter build appbundle --release
+      - |
+        # generate universal apk signed with debug key
+        universal-apk generate \
+          --ks ~/.android/debug.keystore \
+          --ks-pass android \
+          --ks-key-alias androiddebugkey \
+          --key-pass android \
+          --pattern 'build/**/outputs/**/*.aab'
+      - find . -name "Podfile" -execdir pod install \;
+      - flutter build ios --release --no-codesign
+      - |
+        # build web
+        flutter build web --release
+        cd build/web
+        7z a -r ../web.zip ./*
+    artifacts:
+      - build/**/outputs/**/*.apk
+      - build/**/outputs/**/*.aab
+      - build/**/outputs/**/mapping.txt
+      - build/ios/ipa/*.ipa
+      - /tmp/xcodebuild_logs/*.log
+      - build/web.zip
+      - flutter_drive.log
+    publishing:
+      email:
+        recipients:
+          - hafiyyan94@gmail.com
+  staging-workflow:
+    name: Staging Workflow
+    environment:
+      vars:
+        GSERVICE_JSON_PROD: ewogICJwcm9qZWN0X2luZm8iOiB7CiAgICAicHJvamVjdF9udW1iZXIiOiAiMzU3NTY0NjUyOTM2IiwKICAgICJmaXJlYmFzZV91cmwiOiAiaHR0cHM6Ly93YXNwYWRhYmVuY2FuYS1wcm9kLmZpcmViYXNlaW8uY29tIiwKICAgICJwcm9qZWN0X2lkIjogIndhc3BhZGFiZW5jYW5hLXByb2QiLAogICAgInN0b3JhZ2VfYnVja2V0IjogIndhc3BhZGFiZW5jYW5hLXByb2QuYXBwc3BvdC5jb20iCiAgfSwKICAiY2xpZW50IjogWwogICAgewogICAgICAiY2xpZW50X2luZm8iOiB7CiAgICAgICAgIm1vYmlsZXNka19hcHBfaWQiOiAiMTozNTc1NjQ2NTI5MzY6YW5kcm9pZDo4YjNmNWM1ZWVhYWE1MDJmYThhZTNkIiwKICAgICAgICAiYW5kcm9pZF9jbGllbnRfaW5mbyI6IHsKICAgICAgICAgICJwYWNrYWdlX25hbWUiOiAiaWQuYWMudWkuY3Muc2lnYXAiCiAgICAgICAgfQogICAgICB9LAogICAgICAib2F1dGhfY2xpZW50IjogWwogICAgICAgIHsKICAgICAgICAgICJjbGllbnRfaWQiOiAiMzU3NTY0NjUyOTM2LW8zYTBpdHBlNzYxZjgzbTN0Y2hvOGlldmt2dTdvNHI5LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwKICAgICAgICAgICJjbGllbnRfdHlwZSI6IDMKICAgICAgICB9CiAgICAgIF0sCiAgICAgICJhcGlfa2V5IjogWwogICAgICAgIHsKICAgICAgICAgICJjdXJyZW50X2tleSI6ICJBSXphU3lBRE9qaHNSWURZREdOWVpJU0pTLTVIamtIRHI0S0hJMVEiCiAgICAgICAgfQogICAgICBdLAogICAgICAic2VydmljZXMiOiB7CiAgICAgICAgImFwcGludml0ZV9zZXJ2aWNlIjogewogICAgICAgICAgIm90aGVyX3BsYXRmb3JtX29hdXRoX2NsaWVudCI6IFsKICAgICAgICAgICAgewogICAgICAgICAgICAgICJjbGllbnRfaWQiOiAiMzU3NTY0NjUyOTM2LW8zYTBpdHBlNzYxZjgzbTN0Y2hvOGlldmt2dTdvNHI5LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwKICAgICAgICAgICAgICAiY2xpZW50X3R5cGUiOiAzCiAgICAgICAgICAgIH0KICAgICAgICAgIF0KICAgICAgICB9CiAgICAgIH0KICAgIH0KICBdLAogICJjb25maWd1cmF0aW9uX3ZlcnNpb24iOiAiMSIKfQ==
+        GSERVICE_PLIST_PROD: PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+Q0xJRU5UX0lEPC9rZXk+Cgk8c3RyaW5nPjM1NzU2NDY1MjkzNi1yOWY4ZDUyNnFhYW9pbGU5cDNwdDZwODQ0NnNhOG8yOC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbTwvc3RyaW5nPgoJPGtleT5SRVZFUlNFRF9DTElFTlRfSUQ8L2tleT4KCTxzdHJpbmc+Y29tLmdvb2dsZXVzZXJjb250ZW50LmFwcHMuMzU3NTY0NjUyOTM2LXI5ZjhkNTI2cWFhb2lsZTlwM3B0NnA4NDQ2c2E4bzI4PC9zdHJpbmc+Cgk8a2V5PkFQSV9LRVk8L2tleT4KCTxzdHJpbmc+QUl6YVN5QjZSXzV0YXZnOFRoR0t2MEdhRGxGMHlsUEo0cUhuNGlNPC9zdHJpbmc+Cgk8a2V5PkdDTV9TRU5ERVJfSUQ8L2tleT4KCTxzdHJpbmc+MzU3NTY0NjUyOTM2PC9zdHJpbmc+Cgk8a2V5PlBMSVNUX1ZFUlNJT048L2tleT4KCTxzdHJpbmc+MTwvc3RyaW5nPgoJPGtleT5CVU5ETEVfSUQ8L2tleT4KCTxzdHJpbmc+aWQuYWMudWkuY3Muc2lnYXA8L3N0cmluZz4KCTxrZXk+UFJPSkVDVF9JRDwva2V5PgoJPHN0cmluZz53YXNwYWRhYmVuY2FuYS1wcm9kPC9zdHJpbmc+Cgk8a2V5PlNUT1JBR0VfQlVDS0VUPC9rZXk+Cgk8c3RyaW5nPndhc3BhZGFiZW5jYW5hLXByb2QuYXBwc3BvdC5jb208L3N0cmluZz4KCTxrZXk+SVNfQURTX0VOQUJMRUQ8L2tleT4KCTxmYWxzZT48L2ZhbHNlPgoJPGtleT5JU19BTkFMWVRJQ1NfRU5BQkxFRDwva2V5PgoJPGZhbHNlPjwvZmFsc2U+Cgk8a2V5PklTX0FQUElOVklURV9FTkFCTEVEPC9rZXk+Cgk8dHJ1ZT48L3RydWU+Cgk8a2V5PklTX0dDTV9FTkFCTEVEPC9rZXk+Cgk8dHJ1ZT48L3RydWU+Cgk8a2V5PklTX1NJR05JTl9FTkFCTEVEPC9rZXk+Cgk8dHJ1ZT48L3RydWU+Cgk8a2V5PkdPT0dMRV9BUFBfSUQ8L2tleT4KCTxzdHJpbmc+MTozNTc1NjQ2NTI5MzY6aW9zOmU5NDJjZGUzZDdmNzIyMDZhOGFlM2Q8L3N0cmluZz4KCTxrZXk+REFUQUJBU0VfVVJMPC9rZXk+Cgk8c3RyaW5nPmh0dHBzOi8vd2FzcGFkYWJlbmNhbmEtcHJvZC5maXJlYmFzZWlvLmNvbTwvc3RyaW5nPgo8L2RpY3Q+CjwvcGxpc3Q+
+        GSERVICE_JSON_STAGING: ewogICJwcm9qZWN0X2luZm8iOiB7CiAgICAicHJvamVjdF9udW1iZXIiOiAiNzE3NzI2OTc3NTAyIiwKICAgICJmaXJlYmFzZV91cmwiOiAiaHR0cHM6Ly93YXNwYWRhYmVuY2FuYS1zdGFnaW5nLmZpcmViYXNlaW8uY29tIiwKICAgICJwcm9qZWN0X2lkIjogIndhc3BhZGFiZW5jYW5hLXN0YWdpbmciLAogICAgInN0b3JhZ2VfYnVja2V0IjogIndhc3BhZGFiZW5jYW5hLXN0YWdpbmcuYXBwc3BvdC5jb20iCiAgfSwKICAiY2xpZW50IjogWwogICAgewogICAgICAiY2xpZW50X2luZm8iOiB7CiAgICAgICAgIm1vYmlsZXNka19hcHBfaWQiOiAiMTo3MTc3MjY5Nzc1MDI6YW5kcm9pZDpiMzBmNzc0ZGVlMjBkZTFkYmEyMTRmIiwKICAgICAgICAiYW5kcm9pZF9jbGllbnRfaW5mbyI6IHsKICAgICAgICAgICJwYWNrYWdlX25hbWUiOiAiaWQuYWMudWkuY3Muc2lnYXAiCiAgICAgICAgfQogICAgICB9LAogICAgICAib2F1dGhfY2xpZW50IjogWwogICAgICAgIHsKICAgICAgICAgICJjbGllbnRfaWQiOiAiNzE3NzI2OTc3NTAyLXU5ZWxmazJkcm8wcGRlczlpZDk0ZXFvMGtkNGZncGc4LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwKICAgICAgICAgICJjbGllbnRfdHlwZSI6IDMKICAgICAgICB9CiAgICAgIF0sCiAgICAgICJhcGlfa2V5IjogWwogICAgICAgIHsKICAgICAgICAgICJjdXJyZW50X2tleSI6ICJBSXphU3lESWlXTE5YRTlCeWJLcjRIaUlJZ2FVVVRsdTVBTlNHRE0iCiAgICAgICAgfQogICAgICBdLAogICAgICAic2VydmljZXMiOiB7CiAgICAgICAgImFwcGludml0ZV9zZXJ2aWNlIjogewogICAgICAgICAgIm90aGVyX3BsYXRmb3JtX29hdXRoX2NsaWVudCI6IFsKICAgICAgICAgICAgewogICAgICAgICAgICAgICJjbGllbnRfaWQiOiAiNzE3NzI2OTc3NTAyLXU5ZWxmazJkcm8wcGRlczlpZDk0ZXFvMGtkNGZncGc4LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwKICAgICAgICAgICAgICAiY2xpZW50X3R5cGUiOiAzCiAgICAgICAgICAgIH0KICAgICAgICAgIF0KICAgICAgICB9CiAgICAgIH0KICAgIH0KICBdLAogICJjb25maWd1cmF0aW9uX3ZlcnNpb24iOiAiMSIKfQo=
+        GSERVICE_PLIST_STAGING: PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+Q0xJRU5UX0lEPC9rZXk+Cgk8c3RyaW5nPjcxNzcyNjk3NzUwMi00OTdkcWtsY2RqZ25nNDV1dTljOG44YnBjOWY4czd2Zi5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbTwvc3RyaW5nPgoJPGtleT5SRVZFUlNFRF9DTElFTlRfSUQ8L2tleT4KCTxzdHJpbmc+Y29tLmdvb2dsZXVzZXJjb250ZW50LmFwcHMuNzE3NzI2OTc3NTAyLTQ5N2Rxa2xjZGpnbmc0NXV1OWM4bjhicGM5ZjhzN3ZmPC9zdHJpbmc+Cgk8a2V5PkFQSV9LRVk8L2tleT4KCTxzdHJpbmc+QUl6YVN5Qmpob1ZBNkpUZm9IV1kwRDNKa2JYaVRDQXVCMDBpOVNZPC9zdHJpbmc+Cgk8a2V5PkdDTV9TRU5ERVJfSUQ8L2tleT4KCTxzdHJpbmc+NzE3NzI2OTc3NTAyPC9zdHJpbmc+Cgk8a2V5PlBMSVNUX1ZFUlNJT048L2tleT4KCTxzdHJpbmc+MTwvc3RyaW5nPgoJPGtleT5CVU5ETEVfSUQ8L2tleT4KCTxzdHJpbmc+aWQuYWMudWkuY3Muc2lnYXA8L3N0cmluZz4KCTxrZXk+UFJPSkVDVF9JRDwva2V5PgoJPHN0cmluZz53YXNwYWRhYmVuY2FuYS1zdGFnaW5nPC9zdHJpbmc+Cgk8a2V5PlNUT1JBR0VfQlVDS0VUPC9rZXk+Cgk8c3RyaW5nPndhc3BhZGFiZW5jYW5hLXN0YWdpbmcuYXBwc3BvdC5jb208L3N0cmluZz4KCTxrZXk+SVNfQURTX0VOQUJMRUQ8L2tleT4KCTxmYWxzZT48L2ZhbHNlPgoJPGtleT5JU19BTkFMWVRJQ1NfRU5BQkxFRDwva2V5PgoJPGZhbHNlPjwvZmFsc2U+Cgk8a2V5PklTX0FQUElOVklURV9FTkFCTEVEPC9rZXk+Cgk8dHJ1ZT48L3RydWU+Cgk8a2V5PklTX0dDTV9FTkFCTEVEPC9rZXk+Cgk8dHJ1ZT48L3RydWU+Cgk8a2V5PklTX1NJR05JTl9FTkFCTEVEPC9rZXk+Cgk8dHJ1ZT48L3RydWU+Cgk8a2V5PkdPT0dMRV9BUFBfSUQ8L2tleT4KCTxzdHJpbmc+MTo3MTc3MjY5Nzc1MDI6aW9zOjA0MTgwMzZiMDYzMTE3OWZiYTIxNGY8L3N0cmluZz4KCTxrZXk+REFUQUJBU0VfVVJMPC9rZXk+Cgk8c3RyaW5nPmh0dHBzOi8vd2FzcGFkYWJlbmNhbmEtc3RhZ2luZy5maXJlYmFzZWlvLmNvbTwvc3RyaW5nPgo8L2RpY3Q+CjwvcGxpc3Q+
+        PROD_INDEX_HTML: PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8aGVhZD4KICAgIDxtZXRhIGNoYXJzZXQ9IlVURi04Ii8+CiAgICA8bWV0YSBjb250ZW50PSJJRT1FZGdlIiBodHRwLWVxdWl2PSJYLVVBLUNvbXBhdGlibGUiLz4KICAgIDxtZXRhIG5hbWU9ImRlc2NyaXB0aW9uIiBjb250ZW50PSJBIG5ldyBGbHV0dGVyIHByb2plY3QuIi8+CgogICAgPCEtLSBpT1MgbWV0YSB0YWdzICYgaWNvbnMgLS0+CiAgICA8bWV0YSBuYW1lPSJhcHBsZS1tb2JpbGUtd2ViLWFwcC1jYXBhYmxlIiBjb250ZW50PSJ5ZXMiLz4KICAgIDxtZXRhIG5hbWU9ImFwcGxlLW1vYmlsZS13ZWItYXBwLXN0YXR1cy1iYXItc3R5bGUiIGNvbnRlbnQ9ImJsYWNrIi8+CiAgICA8bWV0YSBuYW1lPSJhcHBsZS1tb2JpbGUtd2ViLWFwcC10aXRsZSIgY29udGVudD0ibW9iaWxlX2FwcHMiLz4KICAgIDxsaW5rIHJlbD0iYXBwbGUtdG91Y2gtaWNvbiIgaHJlZj0iaWNvbnMvSWNvbi0xOTIucG5nIi8+CgogICAgPCEtLSBGYXZpY29uIC0tPgogICAgPGxpbmsgcmVsPSJzaG9ydGN1dCBpY29uIiB0eXBlPSJpbWFnZS9wbmciIGhyZWY9ImZhdmljb24ucG5nIi8+CgogICAgPHRpdGxlPlNpZ2FwIFdhYmFoPC90aXRsZT4KICAgIDxsaW5rIHJlbD0ibWFuaWZlc3QiIGhyZWY9Im1hbmlmZXN0Lmpzb24iLz4KCiAgICA8bWV0YSBuYW1lPSJnb29nbGUtc2lnbmluLWNsaWVudF9pZCIKICAgICAgICAgIGNvbnRlbnQ9IjM0MTIyMTM2MzAwNS1ldjVpam43ZTh2bjNxbnJnanRtZmpjaTVhbjRtdGppai5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIvPgo8L2hlYWQ+Cjxib2R5Pgo8IS0tIEZpcmViYXNlIGRlcGVuZGVuY2llcyBiZWdpbiBoZXJlLS0+CjwhLS0gVGhlIGNvcmUgRmlyZWJhc2UgSlMgU0RLIGlzIGFsd2F5cyByZXF1aXJlZCBhbmQgbXVzdCBiZSBsaXN0ZWQgZmlyc3QgLS0+CjxzY3JpcHQgc3JjPSJodHRwczovL3d3dy5nc3RhdGljLmNvbS9maXJlYmFzZWpzLzcuMTIuMC9maXJlYmFzZS1hcHAuanMiPjwvc2NyaXB0PgoKPCEtLSBUT0RPOiBBZGQgU0RLcyBoZXJlIGlmIHdlIHVzZSBtb3JlIEZpcmViYXNlIHByb2R1Y3RzCmh0dHBzOi8vZmlyZWJhc2UuZ29vZ2xlLmNvbS9kb2NzL3dlYi9zZXR1cCNhdmFpbGFibGUtbGlicmFyaWVzIC0tPgo8c2NyaXB0IHNyYz0iaHR0cHM6Ly93d3cuZ3N0YXRpYy5jb20vZmlyZWJhc2Vqcy83LjEyLjAvZmlyZWJhc2UtYW5hbHl0aWNzLmpzIj48L3NjcmlwdD4KPHNjcmlwdCBzcmM9Imh0dHBzOi8vd3d3LmdzdGF0aWMuY29tL2ZpcmViYXNlanMvNy4xMi4wL2ZpcmViYXNlLWF1dGguanMiPjwvc2NyaXB0Pgo8c2NyaXB0IHNyYz0iaHR0cHM6Ly93d3cuZ3N0YXRpYy5jb20vZmlyZWJhc2Vqcy83LjEyLjAvZmlyZWJhc2UtZmlyZXN0b3JlLmpzIj48L3NjcmlwdD4KCjxzY3JpcHQ+CiAgICAgIC8vIFlvdXIgd2ViIGFwcCdzIEZpcmViYXNlIGNvbmZpZ3VyYXRpb24KICAgICAgY29uc3QgZmlyZWJhc2VDb25maWcgPSB7CiAgICAgICAgYXBpS2V5OiAiQUl6YVN5Q1hlVjZSNlpvaVVrTGtYaGRObF9lNDhiUXZpUWJWRlVRIiwKICAgICAgICBhdXRoRG9tYWluOiAid2FzcGFkYWJlbmNhbmEtcHJvZC5maXJlYmFzZWFwcC5jb20iLAogICAgICAgIGRhdGFiYXNlVVJMOiAiaHR0cHM6Ly93YXNwYWRhYmVuY2FuYS1wcm9kLmZpcmViYXNlaW8uY29tIiwKICAgICAgICBwcm9qZWN0SWQ6ICJ3YXNwYWRhYmVuY2FuYS1wcm9kIiwKICAgICAgICBzdG9yYWdlQnVja2V0OiAid2FzcGFkYWJlbmNhbmEtcHJvZC5hcHBzcG90LmNvbSIsCiAgICAgICAgbWVzc2FnaW5nU2VuZGVySWQ6ICIzNTc1NjQ2NTI5MzYiLAogICAgICAgIGFwcElkOiAiMTozNTc1NjQ2NTI5MzY6d2ViOjBhNDI0NjAzYjQ4ZWZhYjNhOGFlM2QiLAogICAgICAgIG1lYXN1cmVtZW50SWQ6ICJHLUNaNTRNOFMzWkQiCiAgICAgIH07CiAgICAgIC8vIEluaXRpYWxpemUgRmlyZWJhc2UKICAgICAgZmlyZWJhc2UuaW5pdGlhbGl6ZUFwcChmaXJlYmFzZUNvbmZpZyk7CiAgICAgIGZpcmViYXNlLmFuYWx5dGljcygpOwoKPC9zY3JpcHQ+CjwhLS0gRmlyZWJhc2UgZGVwZW5kZW5jaWVzIGVuZC0tPgoKPCEtLSBUaGlzIHNjcmlwdCBpbnN0YWxscyBzZXJ2aWNlX3dvcmtlci5qcyB0byBwcm92aWRlIFBXQSBmdW5jdGlvbmFsaXR5IHRvCiAgIGFwcGxpY2F0aW9uLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiwgc2VlOgogICBodHRwczovL2RldmVsb3BlcnMuZ29vZ2xlLmNvbS93ZWIvZnVuZGFtZW50YWxzL3ByaW1lcnMvc2VydmljZS13b3JrZXJzIC0tPgo8c2NyaXB0PgogICAgICBpZiAoInNlcnZpY2VXb3JrZXIiIGluIG5hdmlnYXRvcikgewogICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCJsb2FkIiwgZnVuY3Rpb24oKSB7CiAgICAgICAgICBuYXZpZ2F0b3Iuc2VydmljZVdvcmtlci5yZWdpc3RlcigiL2ZsdXR0ZXJfc2VydmljZV93b3JrZXIuanMiKTsKICAgICAgICB9KTsKICAgICAgfQoKPC9zY3JpcHQ+Cgo8c2NyaXB0IHNyYz0ibWFpbi5kYXJ0LmpzIiB0eXBlPSJhcHBsaWNhdGlvbi9qYXZhc2NyaXB0Ij48L3NjcmlwdD4KPC9ib2R5Pgo8L2h0bWw+Cg==
+      flutter: beta
+      xcode: latest
+      cocoapods: default
+    triggering:
+      events:
+        - push
+      branch_patterns:
+        - pattern: staging-prod-config
+          include: true
+          source: true
+    scripts:
+      - |
+        # set up debug keystore
+        rm -f ~/.android/debug.keystore
+        keytool -genkeypair \
+          -alias androiddebugkey \
+          -keypass android \
+          -keystore ~/.android/debug.keystore \
+          -storepass android \
+          -dname 'CN=Android Debug,O=Android,C=US' \
+          -keyalg 'RSA' \
+          -keysize 2048 \
+          -validity 10000
+      - |
+        # set up local properties
+        echo "flutter.sdk=$HOME/programs/flutter" > "$FCI_BUILD_DIR/android/local.properties"
+      - flutter packages pub get
+      - flutter config --enable-web
+      - |
+        #!/bin/sh
+
+        ls
+        echo $GSERVICE_PLIST_STAGING | base64 -d > ./ios/Runner/GoogleService-Info.plist
+        echo $GSERVICE_JSON_STAGING | base64 -d > ./android/app/google-services.json
+        rm ./web/index.html
+        echo $PROD_INDEX_HTML | base64 -d > ./web/index.html
+      - flutter build appbundle --release --obfuscate --split-debug-info=./logs
+      - |
+        # generate universal apk signed with debug key
+        universal-apk generate \
+          --ks ~/.android/debug.keystore \
+          --ks-pass android \
+          --ks-key-alias androiddebugkey \
+          --key-pass android \
+          --pattern 'build/**/outputs/**/*.aab'
+      - find . -name "Podfile" -execdir pod install \;
+      - flutter build ios --release --obfuscate --split-debug-info=./logs --no-codesign
+      - |
+        # build web
+        flutter build web --release
+        cd build/web
+        7z a -r ../web.zip ./*
+    artifacts:
+      - build/**/outputs/**/*.apk
+      - build/**/outputs/**/*.aab
+      - build/**/outputs/**/mapping.txt
+      - build/ios/ipa/*.ipa
+      - /tmp/xcodebuild_logs/*.log
+      - build/web.zip
+      - flutter_drive.log
+    publishing:
+      email:
+        recipients:
+          - hafiyyan94@gmail.com
diff --git a/firebase.json b/firebase.json
index 66037326c171ff15c5c73bb30860864ed8381291..f5934c66d5ee736bb5c6bc1eae2f0b90201e9829 100644
--- a/firebase.json
+++ b/firebase.json
@@ -12,5 +12,11 @@
         "destination": "/index.html"
       }
     ]
+  },
+  "functions": {
+    "predeploy": [
+      "npm --prefix \"$RESOURCE_DIR\" run lint",
+      "npm --prefix \"$RESOURCE_DIR\" run build"
+    ]
   }
 }
diff --git a/functions/.gitignore b/functions/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7fbb8b40884ecfd72361527c97741f9ddfa8cdaf
--- /dev/null
+++ b/functions/.gitignore
@@ -0,0 +1,8 @@
+## Compiled JavaScript files
+**/*.js
+**/*.js.map
+
+# Typescript v1 declaration files
+typings/
+
+node_modules/
\ No newline at end of file
diff --git a/functions/package-lock.json b/functions/package-lock.json
new file mode 100644
index 0000000000000000000000000000000000000000..80849f5bf5ccf22eb853c47090f47753487e8d8e
--- /dev/null
+++ b/functions/package-lock.json
@@ -0,0 +1,2314 @@
+{
+  "name": "functions",
+  "requires": true,
+  "lockfileVersion": 1,
+  "dependencies": {
+    "@babel/code-frame": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
+      "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+      "dev": true,
+      "requires": {
+        "@babel/highlight": "^7.8.3"
+      }
+    },
+    "@babel/helper-validator-identifier": {
+      "version": "7.9.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz",
+      "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==",
+      "dev": true
+    },
+    "@babel/highlight": {
+      "version": "7.9.0",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
+      "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-validator-identifier": "^7.9.0",
+        "chalk": "^2.0.0",
+        "js-tokens": "^4.0.0"
+      }
+    },
+    "@firebase/app-types": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.0.tgz",
+      "integrity": "sha512-ld6rzjXk/SUauHiQZJkeuSJpxIZ5wdnWuF5fWBFQNPaxsaJ9kyYg9GqEvwZ1z2e6JP5cU9gwRBlfW1WkGtGDYA=="
+    },
+    "@firebase/auth-interop-types": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.4.tgz",
+      "integrity": "sha512-CLKNS84KGAv5lRnHTQZFWoR11Ti7gIPFirDDXWek/fSU+TdYdnxJFR5XSD4OuGyzUYQ3Dq7aVj5teiRdyBl9hA=="
+    },
+    "@firebase/component": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.8.tgz",
+      "integrity": "sha512-kzuCF+NVympQk3gcsHldOmDRVPVndECi6O9Wvd47HTEQYO9HsZWfOM1fHUvvHAijSzNi16p4NSM7UziuBQBL4w==",
+      "requires": {
+        "@firebase/util": "0.2.43",
+        "tslib": "1.11.1"
+      }
+    },
+    "@firebase/database": {
+      "version": "0.5.24",
+      "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.5.24.tgz",
+      "integrity": "sha512-9whAQzU8cxDUKGBWCT/aHVmqfyzCP2RkGhbZi2oHpMrmvht7cuBtXtUbDD5R8WomniCOUP8rtQfmCFI7V9ehYw==",
+      "requires": {
+        "@firebase/auth-interop-types": "0.1.4",
+        "@firebase/component": "0.1.8",
+        "@firebase/database-types": "0.4.14",
+        "@firebase/logger": "0.2.0",
+        "@firebase/util": "0.2.43",
+        "faye-websocket": "0.11.3",
+        "tslib": "1.11.1"
+      }
+    },
+    "@firebase/database-types": {
+      "version": "0.4.14",
+      "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.4.14.tgz",
+      "integrity": "sha512-+D41HWac0HcvwMi+0dezEdSOZHpVjPKPNmpQiW2GDuS5kk27/v1jxc9v7F4ALLtpxbVcn16UZl5PqEkcS9H2Xg==",
+      "requires": {
+        "@firebase/app-types": "0.6.0"
+      }
+    },
+    "@firebase/logger": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.2.0.tgz",
+      "integrity": "sha512-qOMnAh1JY9NkYUEy3iFviiFq0dCvk6qN2DsRy2Y7eAhHR6RqwA47l1kI+0MIXmSzlJ9akXjWAXxV5ijzr68Big=="
+    },
+    "@firebase/util": {
+      "version": "0.2.43",
+      "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.43.tgz",
+      "integrity": "sha512-4gGlvcoOJ48xO6PH59UOHLjvImdYXANF/1d0ao60fbiJDIKxJqMksXw3UF2zsUrRkyCOqIDLeiVuF18vffXP+g==",
+      "requires": {
+        "tslib": "1.11.1"
+      }
+    },
+    "@google-cloud/common": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.4.0.tgz",
+      "integrity": "sha512-zWFjBS35eI9leAHhjfeOYlK5Plcuj/77EzstnrJIZbKgF/nkqjcQuGiMCpzCwOfPyUbz8ZaEOYgbHa759AKbjg==",
+      "optional": true,
+      "requires": {
+        "@google-cloud/projectify": "^1.0.0",
+        "@google-cloud/promisify": "^1.0.0",
+        "arrify": "^2.0.0",
+        "duplexify": "^3.6.0",
+        "ent": "^2.2.0",
+        "extend": "^3.0.2",
+        "google-auth-library": "^5.5.0",
+        "retry-request": "^4.0.0",
+        "teeny-request": "^6.0.0"
+      }
+    },
+    "@google-cloud/firestore": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-3.7.1.tgz",
+      "integrity": "sha512-2zDGr3wnzgMf/sn+wgqLJoakKbchqrn1F05O0CrXdr3pmOpRCTDWD+ua/k73JG/fqWGkoLw+uuDQew980ZHlvw==",
+      "optional": true,
+      "requires": {
+        "deep-equal": "^2.0.0",
+        "functional-red-black-tree": "^1.0.1",
+        "google-gax": "^1.13.0",
+        "readable-stream": "^3.4.0",
+        "through2": "^3.0.0"
+      }
+    },
+    "@google-cloud/paginator": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.3.tgz",
+      "integrity": "sha512-kp/pkb2p/p0d8/SKUu4mOq8+HGwF8NPzHWkj+VKrIPQPyMRw8deZtrO/OcSiy9C/7bpfU5Txah5ltUNfPkgEXg==",
+      "optional": true,
+      "requires": {
+        "arrify": "^2.0.0",
+        "extend": "^3.0.2"
+      }
+    },
+    "@google-cloud/projectify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.4.tgz",
+      "integrity": "sha512-ZdzQUN02eRsmTKfBj9FDL0KNDIFNjBn/d6tHQmA/+FImH5DO6ZV8E7FzxMgAUiVAUq41RFAkb25p1oHOZ8psfg==",
+      "optional": true
+    },
+    "@google-cloud/promisify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.4.tgz",
+      "integrity": "sha512-VccZDcOql77obTnFh0TbNED/6ZbbmHDf8UMNnzO1d5g9V0Htfm4k5cllY8P1tJsRKC3zWYGRLaViiupcgVjBoQ==",
+      "optional": true
+    },
+    "@google-cloud/storage": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-4.6.0.tgz",
+      "integrity": "sha512-ubhbLAnj+hrp32x5gI+JajKU0kvhApA6PsLOLkuOj4Cz4b6MNsyhSWZ5rq2W7TylqfNNW8M9QxPCKWg3Sb0IbA==",
+      "optional": true,
+      "requires": {
+        "@google-cloud/common": "^2.1.1",
+        "@google-cloud/paginator": "^2.0.0",
+        "@google-cloud/promisify": "^1.0.0",
+        "arrify": "^2.0.0",
+        "compressible": "^2.0.12",
+        "concat-stream": "^2.0.0",
+        "date-and-time": "^0.12.0",
+        "duplexify": "^3.5.0",
+        "extend": "^3.0.2",
+        "gaxios": "^2.0.1",
+        "gcs-resumable-upload": "^2.2.4",
+        "hash-stream-validation": "^0.2.2",
+        "mime": "^2.2.0",
+        "mime-types": "^2.0.8",
+        "onetime": "^5.1.0",
+        "p-limit": "^2.2.0",
+        "pumpify": "^2.0.0",
+        "readable-stream": "^3.4.0",
+        "snakeize": "^0.1.0",
+        "stream-events": "^1.0.1",
+        "through2": "^3.0.0",
+        "xdg-basedir": "^4.0.0"
+      }
+    },
+    "@grpc/grpc-js": {
+      "version": "0.6.18",
+      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-0.6.18.tgz",
+      "integrity": "sha512-uAzv/tM8qpbf1vpx1xPMfcUMzbfdqJtdCYAqY/LsLeQQlnTb4vApylojr+wlCyr7bZeg3AFfHvtihnNOQQt/nA==",
+      "optional": true,
+      "requires": {
+        "semver": "^6.2.0"
+      }
+    },
+    "@grpc/proto-loader": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.3.tgz",
+      "integrity": "sha512-8qvUtGg77G2ZT2HqdqYoM/OY97gQd/0crSG34xNmZ4ZOsv3aQT/FQV9QfZPazTGna6MIoyUd+u6AxsoZjJ/VMQ==",
+      "optional": true,
+      "requires": {
+        "lodash.camelcase": "^4.3.0",
+        "protobufjs": "^6.8.6"
+      }
+    },
+    "@protobufjs/aspromise": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+      "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=",
+      "optional": true
+    },
+    "@protobufjs/base64": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+      "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+      "optional": true
+    },
+    "@protobufjs/codegen": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+      "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
+      "optional": true
+    },
+    "@protobufjs/eventemitter": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+      "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=",
+      "optional": true
+    },
+    "@protobufjs/fetch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+      "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
+      "optional": true,
+      "requires": {
+        "@protobufjs/aspromise": "^1.1.1",
+        "@protobufjs/inquire": "^1.1.0"
+      }
+    },
+    "@protobufjs/float": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+      "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=",
+      "optional": true
+    },
+    "@protobufjs/inquire": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+      "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=",
+      "optional": true
+    },
+    "@protobufjs/path": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+      "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=",
+      "optional": true
+    },
+    "@protobufjs/pool": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+      "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=",
+      "optional": true
+    },
+    "@protobufjs/utf8": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+      "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=",
+      "optional": true
+    },
+    "@tootallnate/once": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.0.0.tgz",
+      "integrity": "sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA==",
+      "optional": true
+    },
+    "@types/body-parser": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
+      "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
+      "requires": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "@types/connect": {
+      "version": "3.4.33",
+      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
+      "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@types/express": {
+      "version": "4.17.3",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.3.tgz",
+      "integrity": "sha512-I8cGRJj3pyOLs/HndoP+25vOqhqWkAZsWMEmq1qXy/b/M3ppufecUwaK2/TVDVxcV61/iSdhykUjQQ2DLSrTdg==",
+      "requires": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "*",
+        "@types/serve-static": "*"
+      }
+    },
+    "@types/express-serve-static-core": {
+      "version": "4.17.3",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.3.tgz",
+      "integrity": "sha512-sHEsvEzjqN+zLbqP+8OXTipc10yH1QLR+hnr5uw29gi9AhCAAAdri8ClNV7iMdrJrIzXIQtlkPvq8tJGhj3QJQ==",
+      "requires": {
+        "@types/node": "*",
+        "@types/range-parser": "*"
+      }
+    },
+    "@types/fs-extra": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz",
+      "integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==",
+      "optional": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@types/lodash": {
+      "version": "4.14.149",
+      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz",
+      "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==",
+      "dev": true
+    },
+    "@types/long": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
+      "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==",
+      "optional": true
+    },
+    "@types/mime": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
+      "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw=="
+    },
+    "@types/node": {
+      "version": "8.10.59",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.59.tgz",
+      "integrity": "sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ=="
+    },
+    "@types/range-parser": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
+      "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
+    },
+    "@types/serve-static": {
+      "version": "1.13.3",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
+      "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==",
+      "requires": {
+        "@types/express-serve-static-core": "*",
+        "@types/mime": "*"
+      }
+    },
+    "abort-controller": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+      "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+      "optional": true,
+      "requires": {
+        "event-target-shim": "^5.0.0"
+      }
+    },
+    "accepts": {
+      "version": "1.3.7",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+      "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+      "requires": {
+        "mime-types": "~2.1.24",
+        "negotiator": "0.6.2"
+      }
+    },
+    "agent-base": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz",
+      "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==",
+      "optional": true,
+      "requires": {
+        "debug": "4"
+      }
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^1.9.0"
+      }
+    },
+    "argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dev": true,
+      "requires": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+    },
+    "arrify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+      "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+      "optional": true
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "base64-js": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+      "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
+      "optional": true
+    },
+    "bignumber.js": {
+      "version": "7.2.1",
+      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz",
+      "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==",
+      "optional": true
+    },
+    "body-parser": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+      "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+      "requires": {
+        "bytes": "3.1.0",
+        "content-type": "~1.0.4",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
+        "on-finished": "~2.3.0",
+        "qs": "6.7.0",
+        "raw-body": "2.4.0",
+        "type-is": "~1.6.17"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "buffer-equal-constant-time": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+      "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+    },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+      "optional": true
+    },
+    "builtin-modules": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+      "dev": true
+    },
+    "bytes": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+      "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+    },
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      }
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
+    "commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true
+    },
+    "compressible": {
+      "version": "2.0.18",
+      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+      "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+      "optional": true,
+      "requires": {
+        "mime-db": ">= 1.43.0 < 2"
+      }
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "concat-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
+      "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
+      "optional": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^3.0.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "configstore": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
+      "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
+      "optional": true,
+      "requires": {
+        "dot-prop": "^5.2.0",
+        "graceful-fs": "^4.1.2",
+        "make-dir": "^3.0.0",
+        "unique-string": "^2.0.0",
+        "write-file-atomic": "^3.0.0",
+        "xdg-basedir": "^4.0.0"
+      }
+    },
+    "content-disposition": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+      "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+      "requires": {
+        "safe-buffer": "5.1.2"
+      },
+      "dependencies": {
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        }
+      }
+    },
+    "content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+    },
+    "cookie": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+      "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
+    },
+    "cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+      "optional": true
+    },
+    "cors": {
+      "version": "2.8.5",
+      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+      "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+      "requires": {
+        "object-assign": "^4",
+        "vary": "^1"
+      }
+    },
+    "crypto-random-string": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
+      "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
+      "optional": true
+    },
+    "date-and-time": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.12.0.tgz",
+      "integrity": "sha512-n2RJIAp93AucgF/U/Rz5WRS2Hjg5Z+QxscaaMCi6pVZT1JpJKRH+C08vyH/lRR1kxNXnPxgo3lWfd+jCb/UcuQ==",
+      "optional": true
+    },
+    "debug": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+      "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+      "optional": true,
+      "requires": {
+        "ms": "^2.1.1"
+      }
+    },
+    "deep-equal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.1.tgz",
+      "integrity": "sha512-7Et6r6XfNW61CPPCIYfm1YPGSmh6+CliYeL4km7GWJcpX5LTAflGF8drLLR+MZX+2P3NZfAfSduutBbSWqER4g==",
+      "optional": true,
+      "requires": {
+        "es-abstract": "^1.16.3",
+        "es-get-iterator": "^1.0.1",
+        "is-arguments": "^1.0.4",
+        "is-date-object": "^1.0.1",
+        "is-regex": "^1.0.4",
+        "isarray": "^2.0.5",
+        "object-is": "^1.0.1",
+        "object-keys": "^1.1.1",
+        "regexp.prototype.flags": "^1.2.0",
+        "side-channel": "^1.0.1",
+        "which-boxed-primitive": "^1.0.1",
+        "which-collection": "^1.0.0"
+      }
+    },
+    "define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+      "optional": true,
+      "requires": {
+        "object-keys": "^1.0.12"
+      }
+    },
+    "depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+    },
+    "destroy": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+    },
+    "dicer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz",
+      "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==",
+      "requires": {
+        "streamsearch": "0.1.2"
+      }
+    },
+    "diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true
+    },
+    "dot-prop": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
+      "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
+      "optional": true,
+      "requires": {
+        "is-obj": "^2.0.0"
+      }
+    },
+    "duplexify": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+      "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+      "optional": true,
+      "requires": {
+        "end-of-stream": "^1.0.0",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0",
+        "stream-shift": "^1.0.0"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+          "optional": true
+        },
+        "readable-stream": {
+          "version": "2.3.7",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+          "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+          "optional": true,
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+          "optional": true
+        }
+      }
+    },
+    "ecdsa-sig-formatter": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+      "requires": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+    },
+    "encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+    },
+    "end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "optional": true,
+      "requires": {
+        "once": "^1.4.0"
+      }
+    },
+    "ent": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+      "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=",
+      "optional": true
+    },
+    "es-abstract": {
+      "version": "1.17.5",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
+      "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
+      "optional": true,
+      "requires": {
+        "es-to-primitive": "^1.2.1",
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1",
+        "is-callable": "^1.1.5",
+        "is-regex": "^1.0.5",
+        "object-inspect": "^1.7.0",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.0",
+        "string.prototype.trimleft": "^2.1.1",
+        "string.prototype.trimright": "^2.1.1"
+      }
+    },
+    "es-get-iterator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz",
+      "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==",
+      "optional": true,
+      "requires": {
+        "es-abstract": "^1.17.4",
+        "has-symbols": "^1.0.1",
+        "is-arguments": "^1.0.4",
+        "is-map": "^2.0.1",
+        "is-set": "^2.0.1",
+        "is-string": "^1.0.5",
+        "isarray": "^2.0.5"
+      }
+    },
+    "es-to-primitive": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+      "optional": true,
+      "requires": {
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
+      }
+    },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+      "dev": true
+    },
+    "esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true
+    },
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+    },
+    "event-target-shim": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+      "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+      "optional": true
+    },
+    "express": {
+      "version": "4.17.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+      "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+      "requires": {
+        "accepts": "~1.3.7",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.19.0",
+        "content-disposition": "0.5.3",
+        "content-type": "~1.0.4",
+        "cookie": "0.4.0",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "~1.1.2",
+        "fresh": "0.5.2",
+        "merge-descriptors": "1.0.1",
+        "methods": "~1.1.2",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "~2.0.5",
+        "qs": "6.7.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.1.2",
+        "send": "0.17.1",
+        "serve-static": "1.14.1",
+        "setprototypeof": "1.1.1",
+        "statuses": "~1.5.0",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        }
+      }
+    },
+    "extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "optional": true
+    },
+    "fast-text-encoding": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.1.tgz",
+      "integrity": "sha512-x4FEgaz3zNRtJfLFqJmHWxkMDDvXVtaznj2V9jiP8ACUJrUgist4bP9FmDL2Vew2Y9mEQI/tG4GqabaitYp9CQ==",
+      "optional": true
+    },
+    "faye-websocket": {
+      "version": "0.11.3",
+      "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz",
+      "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==",
+      "requires": {
+        "websocket-driver": ">=0.5.1"
+      }
+    },
+    "finalhandler": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+      "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+      "requires": {
+        "debug": "2.6.9",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "statuses": "~1.5.0",
+        "unpipe": "~1.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "firebase-admin": {
+      "version": "8.10.0",
+      "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-8.10.0.tgz",
+      "integrity": "sha512-QzJZ1sBh9xzKjb44aP6m1duy0Xe1ixexwh0eaOt1CkJYCOq2b6bievK4GNWMl5yGQ7FFBEbZO6hyDi+5wrctcg==",
+      "requires": {
+        "@firebase/database": "^0.5.17",
+        "@google-cloud/firestore": "^3.0.0",
+        "@google-cloud/storage": "^4.1.2",
+        "@types/node": "^8.10.59",
+        "dicer": "^0.3.0",
+        "jsonwebtoken": "8.1.0",
+        "node-forge": "0.7.4"
+      }
+    },
+    "firebase-functions": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-3.5.0.tgz",
+      "integrity": "sha512-BnhfsMyhi2eQfKO6RlOKg7bidiIzTrKY+7qF3M02c5yCczUaSQN3TvggqxphaZPOdEdPF2OjoBBK9xO2R2RHeg==",
+      "requires": {
+        "@types/express": "^4.17.3",
+        "cors": "^2.8.5",
+        "express": "^4.17.1",
+        "jsonwebtoken": "^8.5.1",
+        "lodash": "^4.17.14"
+      },
+      "dependencies": {
+        "jsonwebtoken": {
+          "version": "8.5.1",
+          "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+          "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+          "requires": {
+            "jws": "^3.2.2",
+            "lodash.includes": "^4.3.0",
+            "lodash.isboolean": "^3.0.3",
+            "lodash.isinteger": "^4.0.4",
+            "lodash.isnumber": "^3.0.3",
+            "lodash.isplainobject": "^4.0.6",
+            "lodash.isstring": "^4.0.1",
+            "lodash.once": "^4.0.0",
+            "ms": "^2.1.1",
+            "semver": "^5.6.0"
+          }
+        },
+        "jwa": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+          "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+          "requires": {
+            "buffer-equal-constant-time": "1.0.1",
+            "ecdsa-sig-formatter": "1.0.11",
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "jws": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+          "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+          "requires": {
+            "jwa": "^1.4.1",
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+        }
+      }
+    },
+    "firebase-functions-test": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/firebase-functions-test/-/firebase-functions-test-0.1.7.tgz",
+      "integrity": "sha512-/zVQhaUZ+M7z25aUaZSIah0MIDZIfnRfQxtHYTE8hgUgODmKdaMX20vh5Gv23hnCPauIHuYb7XFTUOZiWU1udA==",
+      "dev": true,
+      "requires": {
+        "@types/lodash": "^4.14.104",
+        "lodash": "^4.17.5"
+      }
+    },
+    "forwarded": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+    },
+    "fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "optional": true
+    },
+    "functional-red-black-tree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+      "optional": true
+    },
+    "gaxios": {
+      "version": "2.3.4",
+      "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.4.tgz",
+      "integrity": "sha512-US8UMj8C5pRnao3Zykc4AAVr+cffoNKRTg9Rsf2GiuZCW69vgJj38VK2PzlPuQU73FZ/nTk9/Av6/JGcE1N9vA==",
+      "optional": true,
+      "requires": {
+        "abort-controller": "^3.0.0",
+        "extend": "^3.0.2",
+        "https-proxy-agent": "^5.0.0",
+        "is-stream": "^2.0.0",
+        "node-fetch": "^2.3.0"
+      }
+    },
+    "gcp-metadata": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.5.0.tgz",
+      "integrity": "sha512-ZQf+DLZ5aKcRpLzYUyBS3yo3N0JSa82lNDO8rj3nMSlovLcz2riKFBsYgDzeXcv75oo5eqB2lx+B14UvPoCRnA==",
+      "optional": true,
+      "requires": {
+        "gaxios": "^2.1.0",
+        "json-bigint": "^0.3.0"
+      }
+    },
+    "gcs-resumable-upload": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-2.3.3.tgz",
+      "integrity": "sha512-sf896I5CC/1AxeaGfSFg3vKMjUq/r+A3bscmVzZm10CElyRanN0XwPu/MxeIO4LSP+9uF6yKzXvNsaTsMXUG6Q==",
+      "optional": true,
+      "requires": {
+        "abort-controller": "^3.0.0",
+        "configstore": "^5.0.0",
+        "gaxios": "^2.0.0",
+        "google-auth-library": "^5.0.0",
+        "pumpify": "^2.0.0",
+        "stream-events": "^1.0.4"
+      }
+    },
+    "glob": {
+      "version": "7.1.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "google-auth-library": {
+      "version": "5.10.1",
+      "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz",
+      "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==",
+      "optional": true,
+      "requires": {
+        "arrify": "^2.0.0",
+        "base64-js": "^1.3.0",
+        "ecdsa-sig-formatter": "^1.0.11",
+        "fast-text-encoding": "^1.0.0",
+        "gaxios": "^2.1.0",
+        "gcp-metadata": "^3.4.0",
+        "gtoken": "^4.1.0",
+        "jws": "^4.0.0",
+        "lru-cache": "^5.0.0"
+      }
+    },
+    "google-gax": {
+      "version": "1.15.1",
+      "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.15.1.tgz",
+      "integrity": "sha512-1T1PwSZWnbdRusA+NCZMSe56iU6swGvuZuy54eYl9vEHiRXTLYbQmUkWY2CqgYD9Fd/T4WBkUl22+rZG80unyw==",
+      "optional": true,
+      "requires": {
+        "@grpc/grpc-js": "^0.6.18",
+        "@grpc/proto-loader": "^0.5.1",
+        "@types/fs-extra": "^8.0.1",
+        "@types/long": "^4.0.0",
+        "abort-controller": "^3.0.0",
+        "duplexify": "^3.6.0",
+        "google-auth-library": "^5.0.0",
+        "is-stream-ended": "^0.1.4",
+        "lodash.at": "^4.6.0",
+        "lodash.has": "^4.5.2",
+        "node-fetch": "^2.6.0",
+        "protobufjs": "^6.8.9",
+        "retry-request": "^4.0.0",
+        "semver": "^6.0.0",
+        "walkdir": "^0.4.0"
+      }
+    },
+    "google-p12-pem": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz",
+      "integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==",
+      "optional": true,
+      "requires": {
+        "node-forge": "^0.9.0"
+      },
+      "dependencies": {
+        "node-forge": {
+          "version": "0.9.1",
+          "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz",
+          "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ==",
+          "optional": true
+        }
+      }
+    },
+    "graceful-fs": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+      "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
+      "optional": true
+    },
+    "gtoken": {
+      "version": "4.1.4",
+      "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.4.tgz",
+      "integrity": "sha512-VxirzD0SWoFUo5p8RDP8Jt2AGyOmyYcT/pOUgDKJCK+iSw0TMqwrVfY37RXTNmoKwrzmDHSk0GMT9FsgVmnVSA==",
+      "optional": true,
+      "requires": {
+        "gaxios": "^2.1.0",
+        "google-p12-pem": "^2.0.0",
+        "jws": "^4.0.0",
+        "mime": "^2.2.0"
+      }
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "optional": true,
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+      "dev": true
+    },
+    "has-symbols": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+      "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+      "optional": true
+    },
+    "hash-stream-validation": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/hash-stream-validation/-/hash-stream-validation-0.2.2.tgz",
+      "integrity": "sha512-cMlva5CxWZOrlS/cY0C+9qAzesn5srhFA8IT1VPiHc9bWWBLkJfEUIZr7MWoi89oOOGmpg8ymchaOjiArsGu5A==",
+      "optional": true,
+      "requires": {
+        "through2": "^2.0.0"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+          "optional": true
+        },
+        "readable-stream": {
+          "version": "2.3.7",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+          "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+          "optional": true,
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+          "optional": true
+        },
+        "through2": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+          "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+          "optional": true,
+          "requires": {
+            "readable-stream": "~2.3.6",
+            "xtend": "~4.0.1"
+          }
+        }
+      }
+    },
+    "http-errors": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+      "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+      "requires": {
+        "depd": "~1.1.2",
+        "inherits": "2.0.3",
+        "setprototypeof": "1.1.1",
+        "statuses": ">= 1.5.0 < 2",
+        "toidentifier": "1.0.0"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+        }
+      }
+    },
+    "http-parser-js": {
+      "version": "0.4.10",
+      "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz",
+      "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q="
+    },
+    "http-proxy-agent": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+      "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+      "optional": true,
+      "requires": {
+        "@tootallnate/once": "1",
+        "agent-base": "6",
+        "debug": "4"
+      }
+    },
+    "https-proxy-agent": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+      "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+      "optional": true,
+      "requires": {
+        "agent-base": "6",
+        "debug": "4"
+      }
+    },
+    "iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      }
+    },
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+      "optional": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+    },
+    "is-arguments": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
+      "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
+      "optional": true
+    },
+    "is-bigint": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz",
+      "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==",
+      "optional": true
+    },
+    "is-boolean-object": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz",
+      "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==",
+      "optional": true
+    },
+    "is-callable": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
+      "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
+      "optional": true
+    },
+    "is-date-object": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+      "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+      "optional": true
+    },
+    "is-map": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz",
+      "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==",
+      "optional": true
+    },
+    "is-number-object": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz",
+      "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==",
+      "optional": true
+    },
+    "is-obj": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+      "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+      "optional": true
+    },
+    "is-regex": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
+      "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+      "optional": true,
+      "requires": {
+        "has": "^1.0.3"
+      }
+    },
+    "is-set": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz",
+      "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==",
+      "optional": true
+    },
+    "is-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
+      "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
+      "optional": true
+    },
+    "is-stream-ended": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz",
+      "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==",
+      "optional": true
+    },
+    "is-string": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
+      "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+      "optional": true
+    },
+    "is-symbol": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+      "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+      "optional": true,
+      "requires": {
+        "has-symbols": "^1.0.1"
+      }
+    },
+    "is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+      "optional": true
+    },
+    "is-weakmap": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
+      "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
+      "optional": true
+    },
+    "is-weakset": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz",
+      "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==",
+      "optional": true
+    },
+    "isarray": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+      "optional": true
+    },
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "js-yaml": {
+      "version": "3.13.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+      "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+      "dev": true,
+      "requires": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      }
+    },
+    "json-bigint": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz",
+      "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=",
+      "optional": true,
+      "requires": {
+        "bignumber.js": "^7.0.0"
+      }
+    },
+    "jsonwebtoken": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.1.0.tgz",
+      "integrity": "sha1-xjl80uX9WD1lwAeoPce7eOaYK4M=",
+      "requires": {
+        "jws": "^3.1.4",
+        "lodash.includes": "^4.3.0",
+        "lodash.isboolean": "^3.0.3",
+        "lodash.isinteger": "^4.0.4",
+        "lodash.isnumber": "^3.0.3",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.isstring": "^4.0.1",
+        "lodash.once": "^4.0.0",
+        "ms": "^2.0.0",
+        "xtend": "^4.0.1"
+      },
+      "dependencies": {
+        "jwa": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+          "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+          "requires": {
+            "buffer-equal-constant-time": "1.0.1",
+            "ecdsa-sig-formatter": "1.0.11",
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "jws": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+          "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+          "requires": {
+            "jwa": "^1.4.1",
+            "safe-buffer": "^5.0.1"
+          }
+        }
+      }
+    },
+    "jwa": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+      "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+      "optional": true,
+      "requires": {
+        "buffer-equal-constant-time": "1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "jws": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+      "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+      "optional": true,
+      "requires": {
+        "jwa": "^2.0.0",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "lodash": {
+      "version": "4.17.15",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
+    },
+    "lodash.at": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz",
+      "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g=",
+      "optional": true
+    },
+    "lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
+      "optional": true
+    },
+    "lodash.has": {
+      "version": "4.5.2",
+      "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz",
+      "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=",
+      "optional": true
+    },
+    "lodash.includes": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+      "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+    },
+    "lodash.isboolean": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+      "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+    },
+    "lodash.isinteger": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+      "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+    },
+    "lodash.isnumber": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+      "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+    },
+    "lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+    },
+    "lodash.isstring": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+      "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+    },
+    "lodash.once": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+      "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+    },
+    "long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+      "optional": true
+    },
+    "lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "optional": true,
+      "requires": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "make-dir": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
+      "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
+      "optional": true,
+      "requires": {
+        "semver": "^6.0.0"
+      }
+    },
+    "media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+    },
+    "merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+    },
+    "methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+    },
+    "mime": {
+      "version": "2.4.4",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
+      "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
+      "optional": true
+    },
+    "mime-db": {
+      "version": "1.43.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
+      "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
+    },
+    "mime-types": {
+      "version": "2.1.26",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
+      "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+      "requires": {
+        "mime-db": "1.43.0"
+      }
+    },
+    "mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "optional": true
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "minimist": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+      "dev": true
+    },
+    "mkdirp": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
+      "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.5"
+      }
+    },
+    "ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+    },
+    "negotiator": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+      "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+    },
+    "node-fetch": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
+      "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==",
+      "optional": true
+    },
+    "node-forge": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.4.tgz",
+      "integrity": "sha512-8Df0906+tq/omxuCZD6PqhPaQDYuyJ1d+VITgxoIA8zvQd1ru+nMJcDChHH324MWitIgbVkAkQoGEEVJNpn/PA=="
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+    },
+    "object-inspect": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
+      "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
+      "optional": true
+    },
+    "object-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz",
+      "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==",
+      "optional": true
+    },
+    "object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "optional": true
+    },
+    "object.assign": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+      "optional": true,
+      "requires": {
+        "define-properties": "^1.1.2",
+        "function-bind": "^1.1.1",
+        "has-symbols": "^1.0.0",
+        "object-keys": "^1.0.11"
+      }
+    },
+    "on-finished": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "requires": {
+        "ee-first": "1.1.1"
+      }
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "onetime": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
+      "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
+      "optional": true,
+      "requires": {
+        "mimic-fn": "^2.1.0"
+      }
+    },
+    "p-limit": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
+      "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+      "optional": true,
+      "requires": {
+        "p-try": "^2.0.0"
+      }
+    },
+    "p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "optional": true
+    },
+    "parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
+    },
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+      "dev": true
+    },
+    "path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+    },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+      "optional": true
+    },
+    "protobufjs": {
+      "version": "6.8.9",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.9.tgz",
+      "integrity": "sha512-j2JlRdUeL/f4Z6x4aU4gj9I2LECglC+5qR2TrWb193Tla1qfdaNQTZ8I27Pt7K0Ajmvjjpft7O3KWTGciz4gpw==",
+      "optional": true,
+      "requires": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.0",
+        "@types/node": "^10.1.0",
+        "long": "^4.0.0"
+      },
+      "dependencies": {
+        "@types/node": {
+          "version": "10.17.17",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.17.tgz",
+          "integrity": "sha512-gpNnRnZP3VWzzj5k3qrpRC6Rk3H/uclhAVo1aIvwzK5p5cOrs9yEyQ8H/HBsBY0u5rrWxXEiVPQ0dEB6pkjE8Q==",
+          "optional": true
+        }
+      }
+    },
+    "proxy-addr": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
+      "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
+      "requires": {
+        "forwarded": "~0.1.2",
+        "ipaddr.js": "1.9.1"
+      }
+    },
+    "pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "optional": true,
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "pumpify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz",
+      "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==",
+      "optional": true,
+      "requires": {
+        "duplexify": "^4.1.1",
+        "inherits": "^2.0.3",
+        "pump": "^3.0.0"
+      },
+      "dependencies": {
+        "duplexify": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz",
+          "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==",
+          "optional": true,
+          "requires": {
+            "end-of-stream": "^1.4.1",
+            "inherits": "^2.0.3",
+            "readable-stream": "^3.1.1",
+            "stream-shift": "^1.0.0"
+          }
+        }
+      }
+    },
+    "qs": {
+      "version": "6.7.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+      "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+    },
+    "range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+    },
+    "raw-body": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+      "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+      "requires": {
+        "bytes": "3.1.0",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      }
+    },
+    "readable-stream": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+      "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+      "optional": true,
+      "requires": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      }
+    },
+    "regexp.prototype.flags": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
+      "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==",
+      "optional": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.0-next.1"
+      }
+    },
+    "resolve": {
+      "version": "1.15.1",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
+      "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
+      "dev": true,
+      "requires": {
+        "path-parse": "^1.0.6"
+      }
+    },
+    "retry-request": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.1.tgz",
+      "integrity": "sha512-BINDzVtLI2BDukjWmjAIRZ0oglnCAkpP2vQjM3jdLhmT62h0xnQgciPwBRDAvHqpkPT2Wo1XuUyLyn6nbGrZQQ==",
+      "optional": true,
+      "requires": {
+        "debug": "^4.1.1",
+        "through2": "^3.0.1"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
+      "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "semver": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+      "optional": true
+    },
+    "send": {
+      "version": "0.17.1",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+      "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+      "requires": {
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "destroy": "~1.0.4",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "~1.7.2",
+        "mime": "1.6.0",
+        "ms": "2.1.1",
+        "on-finished": "~2.3.0",
+        "range-parser": "~1.2.1",
+        "statuses": "~1.5.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          },
+          "dependencies": {
+            "ms": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+              "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+            }
+          }
+        },
+        "mime": {
+          "version": "1.6.0",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+          "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+        },
+        "ms": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+        }
+      }
+    },
+    "serve-static": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+      "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+      "requires": {
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.17.1"
+      }
+    },
+    "setprototypeof": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+      "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+    },
+    "side-channel": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz",
+      "integrity": "sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==",
+      "optional": true,
+      "requires": {
+        "es-abstract": "^1.17.0-next.1",
+        "object-inspect": "^1.7.0"
+      }
+    },
+    "signal-exit": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+      "optional": true
+    },
+    "snakeize": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz",
+      "integrity": "sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0=",
+      "optional": true
+    },
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "dev": true
+    },
+    "statuses": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+    },
+    "stream-events": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
+      "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+      "optional": true,
+      "requires": {
+        "stubs": "^3.0.0"
+      }
+    },
+    "stream-shift": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+      "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+      "optional": true
+    },
+    "streamsearch": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+      "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
+    },
+    "string.prototype.trimleft": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
+      "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
+      "optional": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "function-bind": "^1.1.1"
+      }
+    },
+    "string.prototype.trimright": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
+      "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
+      "optional": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "function-bind": "^1.1.1"
+      }
+    },
+    "string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "optional": true,
+      "requires": {
+        "safe-buffer": "~5.1.0"
+      },
+      "dependencies": {
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+          "optional": true
+        }
+      }
+    },
+    "stubs": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
+      "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=",
+      "optional": true
+    },
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^3.0.0"
+      }
+    },
+    "teeny-request": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-6.0.3.tgz",
+      "integrity": "sha512-TZG/dfd2r6yeji19es1cUIwAlVD8y+/svB1kAC2Y0bjEyysrfbO8EZvJBRwIE6WkwmUoB7uvWLwTIhJbMXZ1Dw==",
+      "optional": true,
+      "requires": {
+        "http-proxy-agent": "^4.0.0",
+        "https-proxy-agent": "^5.0.0",
+        "node-fetch": "^2.2.0",
+        "stream-events": "^1.0.5",
+        "uuid": "^7.0.0"
+      }
+    },
+    "through2": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz",
+      "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==",
+      "optional": true,
+      "requires": {
+        "readable-stream": "2 || 3"
+      }
+    },
+    "toidentifier": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+      "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+    },
+    "tslib": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
+      "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA=="
+    },
+    "tslint": {
+      "version": "5.20.1",
+      "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz",
+      "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "builtin-modules": "^1.1.1",
+        "chalk": "^2.3.0",
+        "commander": "^2.12.1",
+        "diff": "^4.0.1",
+        "glob": "^7.1.1",
+        "js-yaml": "^3.13.1",
+        "minimatch": "^3.0.4",
+        "mkdirp": "^0.5.1",
+        "resolve": "^1.3.2",
+        "semver": "^5.3.0",
+        "tslib": "^1.8.0",
+        "tsutils": "^2.29.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+          "dev": true
+        }
+      }
+    },
+    "tsutils": {
+      "version": "2.29.0",
+      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+      "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+      "dev": true,
+      "requires": {
+        "tslib": "^1.8.1"
+      }
+    },
+    "type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "requires": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      }
+    },
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+      "optional": true
+    },
+    "typedarray-to-buffer": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+      "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+      "optional": true,
+      "requires": {
+        "is-typedarray": "^1.0.0"
+      }
+    },
+    "typescript": {
+      "version": "3.8.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
+      "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
+      "dev": true
+    },
+    "unique-string": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
+      "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
+      "optional": true,
+      "requires": {
+        "crypto-random-string": "^2.0.0"
+      }
+    },
+    "unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+      "optional": true
+    },
+    "utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+    },
+    "uuid": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz",
+      "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==",
+      "optional": true
+    },
+    "vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+    },
+    "walkdir": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz",
+      "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==",
+      "optional": true
+    },
+    "websocket-driver": {
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz",
+      "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==",
+      "requires": {
+        "http-parser-js": ">=0.4.0 <0.4.11",
+        "safe-buffer": ">=5.1.0",
+        "websocket-extensions": ">=0.1.1"
+      }
+    },
+    "websocket-extensions": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz",
+      "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg=="
+    },
+    "which-boxed-primitive": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz",
+      "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==",
+      "optional": true,
+      "requires": {
+        "is-bigint": "^1.0.0",
+        "is-boolean-object": "^1.0.0",
+        "is-number-object": "^1.0.3",
+        "is-string": "^1.0.4",
+        "is-symbol": "^1.0.2"
+      }
+    },
+    "which-collection": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
+      "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
+      "optional": true,
+      "requires": {
+        "is-map": "^2.0.1",
+        "is-set": "^2.0.1",
+        "is-weakmap": "^2.0.1",
+        "is-weakset": "^2.0.1"
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+    },
+    "write-file-atomic": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+      "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+      "optional": true,
+      "requires": {
+        "imurmurhash": "^0.1.4",
+        "is-typedarray": "^1.0.0",
+        "signal-exit": "^3.0.2",
+        "typedarray-to-buffer": "^3.1.5"
+      }
+    },
+    "xdg-basedir": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
+      "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
+      "optional": true
+    },
+    "xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+    },
+    "yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "optional": true
+    }
+  }
+}
diff --git a/functions/package.json b/functions/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..7f381da3a05bee291082fda6489175779341e61c
--- /dev/null
+++ b/functions/package.json
@@ -0,0 +1,26 @@
+{
+  "name": "functions",
+  "scripts": {
+    "lint": "tslint --project tsconfig.json",
+    "build": "tsc",
+    "serve": "npm run build && firebase emulators:start --only functions",
+    "shell": "npm run build && firebase functions:shell",
+    "start": "npm run shell",
+    "deploy": "firebase deploy --only functions",
+    "logs": "firebase functions:log"
+  },
+  "engines": {
+    "node": "8"
+  },
+  "main": "lib/index.js",
+  "dependencies": {
+    "firebase-admin": "^8.6.0",
+    "firebase-functions": "^3.3.0"
+  },
+  "devDependencies": {
+    "tslint": "^5.12.0",
+    "typescript": "^3.2.2",
+    "firebase-functions-test": "^0.1.6"
+  },
+  "private": true
+}
diff --git a/functions/src/index.ts b/functions/src/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cf85d9bf266af61875b5f3da7bb88ab23f502f1a
--- /dev/null
+++ b/functions/src/index.ts
@@ -0,0 +1,145 @@
+import * as functions from 'firebase-functions';
+import { firestore, initializeApp } from 'firebase-admin';
+
+initializeApp(); // setup firebase config
+
+const FIRESTORE_PREFIX = 'waspada/covid19/';
+
+function getNotifikasiId(time : Date, path : String) {
+    let newPath = path.startsWith(FIRESTORE_PREFIX) ? path.substr(FIRESTORE_PREFIX.length) : path;
+    newPath = newPath.replace(/\//g, '-'); // institusi/47/kebutuhan/9 => institusi-47-kebutuhan-9
+
+    return `${time.toISOString()}-${newPath}`;
+}
+
+export const notifyNewInstitusi = functions.firestore
+    .document('waspada/covid19/institusi/{id}')
+    .onCreate((snapshot, context) => {
+        const institusi = snapshot.data();
+        const institusiId = context.params.id;
+
+        if (institusi === undefined) {
+            console.log('Failed to create new notifikasi for institusi ID:', institusiId);
+            return null;
+        }
+
+        const notifikasiId = getNotifikasiId((snapshot.createTime ? snapshot.createTime.toDate() : new Date()), snapshot.ref.path);
+
+        return firestore().doc(FIRESTORE_PREFIX + 'notifikasi/' + notifikasiId).set({
+            judul: 'Ada Institusi kesehatan baru!',
+            deskripsi: `${institusi.nama} (${institusi.provinsi}) baru saja terdaftar. #YukSIGAP cari tahu kebutuhannya!`,
+            tipe: 'institusi',
+            waktu: snapshot.createTime,
+            objectPath: snapshot.ref,
+        });
+    }
+);
+
+export const notifyNewKebutuhan = functions.firestore
+    .document('/waspada/covid19/institusi/{institusiId}/kebutuhan/{kebutuhanId}')
+    .onCreate((snapshot, context) => {
+        const handler = async (_snapshot : firestore.DocumentSnapshot, _context : functions.EventContext) => {
+            const kebutuhan = _snapshot.data();
+            const institusi = (await _snapshot.ref.parent.parent?.get())?.data();
+            const item = (await kebutuhan?.item.get()).data();
+            const notifikasiId = getNotifikasiId((snapshot.createTime ? snapshot.createTime.toDate() : new Date()), snapshot.ref.path);
+
+            return firestore().doc(FIRESTORE_PREFIX + 'notifikasi/' + notifikasiId).set({
+                judul: `${institusi?.nama} membutuhkan ${kebutuhan?.kebutuhan} ${item.nama}!`,
+                deskripsi: `${institusi?.nama} (${institusi?.provinsi}) membutuhkan ${kebutuhan?.kebutuhan} ${item.nama}. #YukSIGAP mari membantu ${institusi?.nama}.`,
+                tipe: 'kebutuhan',
+                waktu: snapshot.createTime,
+                objectPath: snapshot.ref,
+            });
+        };
+
+        return handler(snapshot, context);
+    });
+
+export const notifyNewSupplier = functions.firestore
+    .document('waspada/covid19/supplier/{id}')
+    .onCreate((snapshot, context) => {
+        const supplier = snapshot.data();
+        const supplierId = context.params.id;
+
+        if (supplier === undefined) {
+            console.log('Failed to create new notifikasi for supplier ID:', supplierId);
+            return null;
+        }
+
+        const notifikasiId = getNotifikasiId(snapshot.createTime ? snapshot.createTime.toDate() : new Date(), snapshot.ref.path);
+
+        return firestore().doc(FIRESTORE_PREFIX + 'notifikasi/' + notifikasiId).set({
+            judul: 'Ada Supplier baru!',
+            deskripsi: `${supplier.nama} baru saja terdaftar sebagai supplier di SIGAP. #YukSIGAP cari tahu barang-barang yang disediakan!`,
+            tipe: 'supplier',
+            waktu: snapshot.createTime,
+            objectPath: snapshot.ref,
+        });
+    }
+);
+
+export const notifyNewHomeIndustry = functions.firestore
+    .document('waspada/covid19/home-industry/{id}')
+    .onCreate((snapshot, context) => {
+        const homeIndustry = snapshot.data();
+        const homeIndustryId = context.params.id;
+
+        if (homeIndustry === undefined) {
+            console.log('Failed to create new notifikasi for homeIndustry ID:', homeIndustryId);
+            return null;
+        }
+
+        const notifikasiId = getNotifikasiId(snapshot.createTime ? snapshot.createTime.toDate() : new Date(), snapshot.ref.path);
+
+        return firestore().doc(FIRESTORE_PREFIX + 'notifikasi/' + notifikasiId).set({
+            judul: 'Ada Home Industry baru!',
+            deskripsi: `${homeIndustry.nama} baru saja terdaftar sebagai home industry di SIGAP. #YukSIGAP cari tahu barang-barang yang disediakan!`,
+            tipe: 'homeIndustry',
+            waktu: snapshot.createTime,
+            objectPath: snapshot.ref,
+        });
+    }
+);
+
+export const notifyNewBarangSupplier = functions.firestore
+    .document('/waspada/covid19/supplier/{supplierId}/barang/{barangId}')
+    .onCreate((snapshot, context) => {
+        const handler = async (_snapshot : firestore.DocumentSnapshot, _context : functions.EventContext) => {
+            const barang = _snapshot.data();
+            const supplier = (await _snapshot.ref.parent.parent?.get())?.data();
+            const item = (await barang?.item.get()).data();
+            const notifikasiId = getNotifikasiId((snapshot.createTime ? snapshot.createTime.toDate() : new Date()), snapshot.ref.path);
+
+            return firestore().doc(FIRESTORE_PREFIX + 'notifikasi/' + notifikasiId).set({
+                judul: `Tersedia ${barang?.stok} ${item.nama}!`,
+                deskripsi: `${supplier?.nama} baru saja menambahkan ${barang?.stok} ${item.nama} baru.`,
+                tipe: 'supplier-barang',
+                waktu: snapshot.createTime,
+                objectPath: snapshot.ref,
+            });
+        };
+
+        return handler(snapshot, context);
+    });
+
+export const notifyNewBarangHomeIndustry = functions.firestore
+    .document('/waspada/covid19/home-industry/{homeIndustryId}/barang/{barangId}')
+    .onCreate((snapshot, context) => {
+        const handler = async (_snapshot : firestore.DocumentSnapshot, _context : functions.EventContext) => {
+            const barang = _snapshot.data();
+            const homeIndsutry = (await _snapshot.ref.parent.parent?.get())?.data();
+            const item = (await barang?.item.get()).data();
+            const notifikasiId = getNotifikasiId((snapshot.createTime ? snapshot.createTime.toDate() : new Date()), snapshot.ref.path);
+
+            return firestore().doc(FIRESTORE_PREFIX + 'notifikasi/' + notifikasiId).set({
+                judul: `Tersedia ${barang?.stok} ${item.nama}!`,
+                deskripsi: `${homeIndsutry?.nama} baru saja menambahkan ${barang?.stok} ${item.nama} baru.`,
+                tipe: 'homeIndustry-barang',
+                waktu: snapshot.createTime,
+                objectPath: snapshot.ref,
+            });
+        };
+
+        return handler(snapshot, context);
+    });
diff --git a/functions/tsconfig.json b/functions/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..7ce05d039d680e58fcf3c03a64992831537b8bda
--- /dev/null
+++ b/functions/tsconfig.json
@@ -0,0 +1,15 @@
+{
+  "compilerOptions": {
+    "module": "commonjs",
+    "noImplicitReturns": true,
+    "noUnusedLocals": true,
+    "outDir": "lib",
+    "sourceMap": true,
+    "strict": true,
+    "target": "es2017"
+  },
+  "compileOnSave": true,
+  "include": [
+    "src"
+  ]
+}
diff --git a/functions/tslint.json b/functions/tslint.json
new file mode 100644
index 0000000000000000000000000000000000000000..98b2bfdc7397e3301d795577d56ad815733c5e51
--- /dev/null
+++ b/functions/tslint.json
@@ -0,0 +1,115 @@
+{
+  "rules": {
+    // -- Strict errors --
+    // These lint rules are likely always a good idea.
+
+    // Force function overloads to be declared together. This ensures readers understand APIs.
+    "adjacent-overload-signatures": true,
+
+    // Do not allow the subtle/obscure comma operator.
+    "ban-comma-operator": true,
+
+    // Do not allow internal modules or namespaces . These are deprecated in favor of ES6 modules.
+    "no-namespace": true,
+
+    // Do not allow parameters to be reassigned. To avoid bugs, developers should instead assign new values to new vars.
+    "no-parameter-reassignment": true,
+
+    // Force the use of ES6-style imports instead of /// <reference path=> imports.
+    "no-reference": true,
+
+    // Do not allow type assertions that do nothing. This is a big warning that the developer may not understand the
+    // code currently being edited (they may be incorrectly handling a different type case that does not exist).
+    "no-unnecessary-type-assertion": true,
+
+    // Disallow nonsensical label usage.
+    "label-position": true,
+
+    // Disallows the (often typo) syntax if (var1 = var2). Replace with if (var2) { var1 = var2 }.
+    "no-conditional-assignment": true,
+
+    // Disallows constructors for primitive types (e.g. new Number('123'), though Number('123') is still allowed).
+    "no-construct": true,
+
+    // Do not allow super() to be called twice in a constructor.
+    "no-duplicate-super": true,
+
+    // Do not allow the same case to appear more than once in a switch block.
+    "no-duplicate-switch-case": true,
+
+    // Do not allow a variable to be declared more than once in the same block. Consider function parameters in this
+    // rule.
+    "no-duplicate-variable": [true, "check-parameters"],
+
+    // Disallows a variable definition in an inner scope from shadowing a variable in an outer scope. Developers should
+    // instead use a separate variable name.
+    "no-shadowed-variable": true,
+
+    // Empty blocks are almost never needed. Allow the one general exception: empty catch blocks.
+    "no-empty": [true, "allow-empty-catch"],
+
+    // Functions must either be handled directly (e.g. with a catch() handler) or returned to another function.
+    // This is a major source of errors in Cloud Functions and the team strongly recommends leaving this rule on.
+    "no-floating-promises": true,
+
+    // Do not allow any imports for modules that are not in package.json. These will almost certainly fail when
+    // deployed.
+    "no-implicit-dependencies": true,
+
+    // The 'this' keyword can only be used inside of classes.
+    "no-invalid-this": true,
+
+    // Do not allow strings to be thrown because they will not include stack traces. Throw Errors instead.
+    "no-string-throw": true,
+
+    // Disallow control flow statements, such as return, continue, break, and throw in finally blocks.
+    "no-unsafe-finally": true,
+
+    // Expressions must always return a value. Avoids common errors like const myValue = functionReturningVoid();
+    "no-void-expression": [true, "ignore-arrow-function-shorthand"],
+
+    // Disallow duplicate imports in the same file.
+    "no-duplicate-imports": true,
+
+
+    // -- Strong Warnings --
+    // These rules should almost never be needed, but may be included due to legacy code.
+    // They are left as a warning to avoid frustration with blocked deploys when the developer
+    // understand the warning and wants to deploy anyway.
+
+    // Warn when an empty interface is defined. These are generally not useful.
+    "no-empty-interface": {"severity": "warning"},
+
+    // Warn when an import will have side effects.
+    "no-import-side-effect": {"severity": "warning"},
+
+    // Warn when variables are defined with var. Var has subtle meaning that can lead to bugs. Strongly prefer const for
+    // most values and let for values that will change.
+    "no-var-keyword": {"severity": "warning"},
+
+    // Prefer === and !== over == and !=. The latter operators support overloads that are often accidental.
+    "triple-equals": {"severity": "warning"},
+
+    // Warn when using deprecated APIs.
+    "deprecation": {"severity": "warning"},
+
+    // -- Light Warnings --
+    // These rules are intended to help developers use better style. Simpler code has fewer bugs. These would be "info"
+    // if TSLint supported such a level.
+
+    // prefer for( ... of ... ) to an index loop when the index is only used to fetch an object from an array.
+    // (Even better: check out utils like .map if transforming an array!)
+    "prefer-for-of": {"severity": "warning"},
+
+    // Warns if function overloads could be unified into a single function with optional or rest parameters.
+    "unified-signatures": {"severity": "warning"},
+
+    // Prefer const for values that will not change. This better documents code.
+    "prefer-const": {"severity": "warning"},
+
+    // Multi-line object literals and function calls should have a trailing comma. This helps avoid merge conflicts.
+    "trailing-comma": {"severity": "warning"}
+  },
+
+  "defaultSeverity": "error"
+}
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 7dd65135b8f159838c97f8696869ea21dc1a62a2..14e514fa31ab80ec07e4d63bd92c8a1aa956090a 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -3,13 +3,14 @@
 	archiveVersion = 1;
 	classes = {
 	};
-	objectVersion = 46;
+	objectVersion = 50;
 	objects = {
 
 /* Begin PBXBuildFile section */
 		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
 		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
 		74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+		7F94E9BB24286C270007D3CF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7F94E9BA24286C270007D3CF /* GoogleService-Info.plist */; };
 		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 */; };
@@ -35,6 +36,7 @@
 		74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
 		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+		7F94E9BA24286C270007D3CF /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
 		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
 		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
 		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -86,6 +88,7 @@
 		97C146F01CF9000F007C117D /* Runner */ = {
 			isa = PBXGroup;
 			children = (
+				7F94E9BA24286C270007D3CF /* GoogleService-Info.plist */,
 				97C146FA1CF9000F007C117D /* Main.storyboard */,
 				97C146FD1CF9000F007C117D /* Assets.xcassets */,
 				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
@@ -169,6 +172,7 @@
 			files = (
 				97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
 				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+				7F94E9BB24286C270007D3CF /* GoogleService-Info.plist in Resources */,
 				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
 				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
 			);
@@ -303,7 +307,10 @@
 					"$(PROJECT_DIR)/Flutter",
 				);
 				INFOPLIST_FILE = Runner/Info.plist;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Flutter",
@@ -418,7 +425,8 @@
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;
 				SUPPORTED_PLATFORMS = iphoneos;
-				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VALIDATE_PRODUCT = YES;
 			};
@@ -437,7 +445,10 @@
 					"$(PROJECT_DIR)/Flutter",
 				);
 				INFOPLIST_FILE = Runner/Info.plist;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Flutter",
@@ -464,7 +475,10 @@
 					"$(PROJECT_DIR)/Flutter",
 				);
 				INFOPLIST_FILE = Runner/Info.plist;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Flutter",
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index 70693e4a8c128fc4350b157416374ca599ac8c7b..94710963ffbbe08dca84d1864e7c5a33ca7a1914 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -7,7 +7,10 @@ import Flutter
     _ application: UIApplication,
     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
   ) -> Bool {
+    var flutter_native_splash = 1
+    UIApplication.shared.isStatusBarHidden = false
+
     GeneratedPluginRegistrant.register(with: self)
     return super.application(application, didFinishLaunchingWithOptions: launchOptions)
   }
-}
+}
\ No newline at end of file
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
index dc9ada4725e9b0ddb1deab583e5b5102493aa332..6528e6296a62f938984e29f123575584042823c7 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png 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
index 28c6bf03016f6c994b70f38d1b7346e5831b531f..47ca6550ad097215c7becd84b56862290fe897f8 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png 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
index 2ccbfd967d9697cd4b83225558af2911e9571c9b..08b819d8025aaafca97c9395ec3b4a646efa19e8 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png 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
index f091b6b0bca859a3f474b03065bef75ba58a9e4c..d6c5e125ef50b0dce661a073ffc11f9ef61468c4 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png 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
index 4cde12118dda48d71e01fcb589a74d069c5d7cb5..057aa4e0e24e161e7bf4541b396febac1125fcfe 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png 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
index d0ef06e7edb86cdfe0d15b4b0d98334a86163658..7ae9d161adeb7f15625b1aafd803934fbe6bc656 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png 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
index dcdc2306c28505ebc0b6c3a359c4d252bf626b9f..f4c42061e095cb4e31d7922fd44eef2e4b9d4b52 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png 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
index 2ccbfd967d9697cd4b83225558af2911e9571c9b..08b819d8025aaafca97c9395ec3b4a646efa19e8 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png 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
index c8f9ed8f5cee1c98386d13b17e89f719e83555b2..051a221d4372d14b25ff23b4cfee12439bec987b 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png 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
index a6d6b8609df07bf62e5100a53a01510388bd2b22..f321375c0373ac3f1596bcd08729de5894a7e1e8 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png 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 0000000000000000000000000000000000000000..87863c14a2141ee0a522165cdd91f48bb78341e6
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 0000000000000000000000000000000000000000..b493acfeffe900249c6139875497fde9d522b659
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 0000000000000000000000000000000000000000..9dce17051bfa77fa3f5dead6536824c2f9c395a0
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 0000000000000000000000000000000000000000..2213d056c49a352b7c5c139c970564058631f37c
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
index a6d6b8609df07bf62e5100a53a01510388bd2b22..f321375c0373ac3f1596bcd08729de5894a7e1e8 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png 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
index 75b2d164a5a98e212cca15ea7bf2ab5de5108680..e18a20e07c038f550e42261f13d9b9d37a644406 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png 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 0000000000000000000000000000000000000000..1d96626de9c8e9148fca2153d1c9275e80111085
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 0000000000000000000000000000000000000000..e2da3978ac1865498f9bddc3087e92cd32a1f2ad
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
index c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..4551738a6f5e8a9e44140055899be01d440852a5 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png 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
index 6a84f41e14e27f4b11f16f9ee39279ac98f8d5ac..429b794b94905a03b336d7738cfbd9e7b861a7a6 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png 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
index d0e1f58536026aebc4f1f70e481f6993c9ff088d..b8ea8aad13df348ac9581ed13de67e083625f696 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..78a6bb8380ed11e0aa18362d8ae7f0b1d9beec52 100644
Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png 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
index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..1f479771d583acc37d21530367c4523fd352ea15 100644
Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png 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
index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..a54282aa578f1876e716a1fc18e17b326803ad56 100644
Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard
index f2e259c7c9390ff69a6bbe1e0907e6dc366848e7..cf7627f3edf9d890b2168c2ab2801f1395855e1e 100644
--- a/ios/Runner/Base.lproj/LaunchScreen.storyboard
+++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -19,7 +19,7 @@
                             <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
                             </imageView>
                         </subviews>
-                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <color key="backgroundColor" red="0.996078431372549" green="0.996078431372549" blue="0.996078431372549" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                         <constraints>
                             <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
                             <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
@@ -32,6 +32,6 @@
         </scene>
     </scenes>
     <resources>
-        <image name="LaunchImage" width="168" height="185"/>
+        <image name="LaunchImage" width="576" height="712"/>
     </resources>
-</document>
+</document>
\ No newline at end of file
diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist
new file mode 100644
index 0000000000000000000000000000000000000000..ed9d5d9b234683bcd4f003daac01c95e0cc74932
--- /dev/null
+++ b/ios/Runner/GoogleService-Info.plist
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CLIENT_ID</key>
+	<string>717726977502-497dqklcdjgng45uu9c8n8bpc9f8s7vf.apps.googleusercontent.com</string>
+	<key>REVERSED_CLIENT_ID</key>
+	<string>com.googleusercontent.apps.717726977502-497dqklcdjgng45uu9c8n8bpc9f8s7vf</string>
+	<key>API_KEY</key>
+	<string>AIzaSyBjhoVA6JTfoHWY0D3JkbXiTCAuB00i9SY</string>
+	<key>GCM_SENDER_ID</key>
+	<string>717726977502</string>
+	<key>PLIST_VERSION</key>
+	<string>1</string>
+	<key>BUNDLE_ID</key>
+	<string>id.ac.ui.cs.sigap</string>
+	<key>PROJECT_ID</key>
+	<string>waspadabencana-staging</string>
+	<key>STORAGE_BUCKET</key>
+	<string>waspadabencana-staging.appspot.com</string>
+	<key>IS_ADS_ENABLED</key>
+	<false></false>
+	<key>IS_ANALYTICS_ENABLED</key>
+	<false></false>
+	<key>IS_APPINVITE_ENABLED</key>
+	<true></true>
+	<key>IS_GCM_ENABLED</key>
+	<true></true>
+	<key>IS_SIGNIN_ENABLED</key>
+	<true></true>
+	<key>GOOGLE_APP_ID</key>
+	<string>1:717726977502:ios:0418036b0631179fba214f</string>
+	<key>DATABASE_URL</key>
+	<string>https://waspadabencana-staging.firebaseio.com</string>
+</dict>
+</plist>
\ No newline at end of file
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index c72e714c497aa94d3ac6fd1a5b6c3980b8196d02..15f26bf81939a79c2d10e959e0ac987f929b4429 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -41,5 +41,8 @@
 	</array>
 	<key>UIViewControllerBasedStatusBarAppearance</key>
 	<false/>
+	<key>UIStatusBarHidden</key>
+	<true/>
+
 </dict>
-</plist>
+</plist>
\ No newline at end of file
diff --git a/lib/main.dart b/lib/main.dart
index 26c981eef358d135d1e378d9d219b793aff419d1..1a47e2399b6e433a8dbe9079915682ce1d91aa87 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,6 +1,11 @@
 import 'package:flutter/material.dart';
+import 'package:intl/date_symbol_data_local.dart';
+import 'package:intl/intl.dart';
 import 'package:mobile_apps/src/app.dart';
+import 'package:mobile_apps/src/routes.dart';
 
 void main() {
-  runApp(App());
+  AppRoute.setupRouter();
+  Intl.defaultLocale = 'id_ID';
+  initializeDateFormatting().then((value) => runApp(App()));
 }
diff --git a/lib/src/app.dart b/lib/src/app.dart
index aaa10414ace492729d7b27fbcc75c702452fb453..18300508eac9af68dfca443a3761e712713495d3 100644
--- a/lib/src/app.dart
+++ b/lib/src/app.dart
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 import 'package:flutter/widgets.dart';
 import 'package:mobile_apps/src/services/auth_service.dart';
 import 'package:provider/provider.dart';
+import 'package:screenshot/screenshot.dart';
 
 import 'routes.dart';
 import 'theme.dart';
@@ -9,16 +10,23 @@ import 'theme.dart';
 class App extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
+    var screenshotController = ScreenshotController();
+
     return MultiProvider(
       providers: [
         // Add service that can be accessed globally here
         Provider<AuthService>(create: (_) => AuthService()),
+        Provider.value(value: screenshotController),
       ],
-      child: MaterialApp(
-        theme: AppTheme.light(),
-        darkTheme: AppTheme.dark(),
-        initialRoute: AppRoutes.login.route,
-        routes: AppRoutes.routes,
+      child: Screenshot(
+        controller: screenshotController,
+        child: MaterialApp(
+          theme: AppTheme.light(),
+          themeMode: ThemeMode.light,
+          darkTheme: AppTheme.dark(),
+          onGenerateRoute: AppRoute.router.generator,
+          initialRoute: AppRoute.home,
+        ),
       ),
     );
   }
diff --git a/lib/src/common/CustomSerializer.dart b/lib/src/common/CustomSerializer.dart
new file mode 100644
index 0000000000000000000000000000000000000000..3c495825b91a9b60971d591bd5f32f6eaa0bbeb9
--- /dev/null
+++ b/lib/src/common/CustomSerializer.dart
@@ -0,0 +1,10 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+
+class CustomSerializer {
+  static DateTime timeStampToDateTime(Timestamp t) => t.toDate();
+
+  static String docReferenceToPath(DocumentReference d) => d.path;
+
+  static Map<String, double> geoPointToMap(GeoPoint g) =>
+      {'lat': g.latitude, 'long': g.longitude};
+}
diff --git a/lib/src/common/constants.dart b/lib/src/common/constants.dart
index 6d1c74e30507041b353c91f5430d134253715f3d..f5868c36a1d751c4956de62714e9fe9047393397 100644
--- a/lib/src/common/constants.dart
+++ b/lib/src/common/constants.dart
@@ -1,4 +1,16 @@
+import 'package:flutter/material.dart';
+
 class MarginConstants {
-  static double horizontalFromScreen = 16.0;
-  static double verticalFromScreen = 16.0;
+  static const double horizontalFromScreen = 16.0;
+  static const double verticalFromScreen = 16.0;
+}
+
+class ColorConstants{
+  static const Color pinkPrimary = Color(0xffEDD7D7);
+  static const Color redPrimary = Color(0xffBA0000);
+  static const Color backgroundColor = Color(0xfff9f9f9);
+  static const Color bluePrimary = Color(0xff3C8DBC);
+  static const Color grayPrimary = Color(0xff5b5b5b);
+  static const Color muteText = Color(0xff6F6F6F);
+  static const Color redSecondary = Color(0xffE16E6E);
 }
diff --git a/lib/src/common/functions.dart b/lib/src/common/functions.dart
new file mode 100644
index 0000000000000000000000000000000000000000..28a6846aee2f21225719b7d24722984b5fd14a0e
--- /dev/null
+++ b/lib/src/common/functions.dart
@@ -0,0 +1,33 @@
+import 'dart:io';
+import 'package:universal_html/html.dart' as html;
+import 'package:esys_flutter_share/esys_flutter_share.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:provider/provider.dart';
+import 'package:screenshot/screenshot.dart';
+import 'package:url_launcher/url_launcher.dart';
+
+void shareScreenshot(BuildContext context, String title) {
+  final screenshotController = Provider.of<ScreenshotController>(context, listen: false);
+  screenshotController.capture(pixelRatio: 1.5).then((File image) {
+    Share.file(
+        'Siaga Wabah',
+        title != null ? 'Siaga_Wabah_$title.png' : 'Siaga_Wabah.png',
+        image.readAsBytesSync(),
+        'image/png');
+  });
+}
+
+Future launchURL(String url, {String scheme}) async {
+  var _url = scheme == null ? url: '${scheme}:${url}';
+  if (await canLaunch(_url)) {
+    await launch(_url);
+  } else {
+    html.window.open('${_url}', scheme);
+  }
+}
+
+String cleanPhoneNumber(String phoneNumber) {
+  return phoneNumber
+      .replaceAll(RegExp(r'[^0-9]'), '')
+      .replaceAll(RegExp(r'^0+'), '');
+}
diff --git a/lib/src/components/appbar/sigapappbar.dart b/lib/src/components/appbar/sigapappbar.dart
new file mode 100644
index 0000000000000000000000000000000000000000..68aa62aee5f8279e0df736da6ad8ba7452f1c612
--- /dev/null
+++ b/lib/src/components/appbar/sigapappbar.dart
@@ -0,0 +1,46 @@
+import 'package:flutter/material.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/common/functions.dart';
+import 'package:mobile_apps/src/routes.dart';
+
+class SigapAppBar extends StatelessWidget implements PreferredSizeWidget {
+  final String title;
+  final PreferredSizeWidget bottom;
+  SigapAppBar({this.title, this.bottom});
+
+  @override
+  Size get preferredSize {
+    var height = 56.0;
+    if (bottom != null) {
+      height += bottom.preferredSize.height;
+    }
+    return Size.fromHeight(height);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return AppBar(
+      title: Text(title ?? 'Siaga Wabah',
+          style: TextStyle(fontWeight: FontWeight.bold)),
+      actions: <Widget>[
+        IconButton(
+          icon: Icon(Icons.share),
+          onPressed: () => shareScreenshot(context, title),
+        ),
+        Container(
+          margin:
+              EdgeInsets.only(right: MarginConstants.horizontalFromScreen * 2),
+          child: (title == 'Notifikasi') ? null : IconButton(
+            icon: Icon(Icons.notifications),
+            tooltip: 'Notifikasi',
+            onPressed: () {
+              Navigator.of(context).pushNamed(AppRoute.notification);
+            },
+          ),
+        ),
+      ],
+      centerTitle: true,
+      bottom: bottom,
+    );
+  }
+}
diff --git a/lib/src/components/bottom_navigation_bar.dart b/lib/src/components/bottom_navigation_bar.dart
new file mode 100644
index 0000000000000000000000000000000000000000..809639c6b563be6176d935073493f2681be6282a
--- /dev/null
+++ b/lib/src/components/bottom_navigation_bar.dart
@@ -0,0 +1,77 @@
+import 'package:flutter/material.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/appbar/sigapappbar.dart';
+import 'package:mobile_apps/src/screens/home/home_screen.dart';
+import 'package:mobile_apps/src/screens/ketersediaan_screen/ketersediaan_agregrasi.dart';
+
+
+class BottomNavigation extends StatefulWidget {
+  const BottomNavigation({Key key}) : super(key: key);
+
+  @override
+  _BottomNavigationState createState() => _BottomNavigationState();
+}
+
+class _BottomNavigationState extends State<BottomNavigation> {
+  static const TextStyle optionStyle =
+  TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
+
+  static const List<String> _appBarTitle = <String>[
+    'SIGAP',
+    'KETERSEDIAAN',
+    'Akun'
+  ];
+
+  static const List<Widget> _widgetOptions = <Widget>[
+    HomeScreen(),
+    KetersediaanAgregrasi(),
+    Text(
+      'Akun',
+      style: optionStyle,
+    ),
+  ];
+
+  int _selectedIndex = 0;
+
+  void _onItemTapped(int index) {
+    setState(() {
+      _selectedIndex = index;
+    });
+  }
+
+
+  Widget _dynamicAppBar(BuildContext context) {
+
+    return SigapAppBar(title: _appBarTitle[_selectedIndex],);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: _dynamicAppBar(context),
+      body: _widgetOptions.elementAt(_selectedIndex),
+      backgroundColor: ColorConstants.backgroundColor,
+      bottomNavigationBar: BottomNavigationBar(
+        items: const <BottomNavigationBarItem>[
+          BottomNavigationBarItem(
+              icon: Icon(
+                Icons.home,
+              ),
+              title: Text('Beranda')),
+          BottomNavigationBarItem(
+              icon: Icon(Icons.list), title: Text('Ketersediaan')),
+          BottomNavigationBarItem(
+              icon: Icon(
+                Icons.person_outline,
+              ),
+              title: Text('Akun'))
+        ],
+        currentIndex: _selectedIndex,
+        selectedItemColor: ColorConstants.redPrimary,
+        onTap: _onItemTapped,
+        unselectedLabelStyle: TextStyle(color: Colors.grey),
+        unselectedItemColor: Colors.grey,
+      ),
+    );
+  }
+}
diff --git a/lib/src/components/buttons/buttons.dart b/lib/src/components/buttons/buttons.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c0a60d96643338329da524dea099dac24cb9dd30
--- /dev/null
+++ b/lib/src/components/buttons/buttons.dart
@@ -0,0 +1,108 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+
+class RoundedButton extends StatelessWidget {
+  final double radius;
+  final double elevation;
+  final EdgeInsetsGeometry padding;
+  final Color color;
+  final Color borderColor;
+  final String text;
+  final Widget child;
+  final TextStyle style;
+  final Function onPressed;
+
+  const RoundedButton(
+      {this.text,
+      this.radius = 18.0,
+      this.elevation = 1.0,
+      this.color = Colors.red,
+      this.padding,
+      this.style,
+      this.onPressed,
+      this.borderColor,
+      this.child});
+
+  @override
+  Widget build(BuildContext context) {
+    return RaisedButton(
+      shape: RoundedRectangleBorder(
+          borderRadius: BorderRadius.circular(radius),
+          side: BorderSide(
+              color: onPressed == null
+                  ? Theme.of(context).disabledColor
+                  : borderColor ?? color)),
+      padding: padding ?? Theme.of(context).buttonTheme.padding,
+      elevation: elevation,
+      focusElevation: 2 * elevation,
+      hoverElevation: 2 * elevation,
+      highlightElevation: 4 * elevation,
+      color: color,
+      child: child ??
+          Text(
+            text,
+            style: style ??
+                TextStyle(
+                  fontSize: 16,
+                  color: Colors.white,
+                  fontWeight: FontWeight.w800,
+                ),
+          ),
+      onPressed: onPressed,
+    );
+  }
+}
+
+class IconArrowButton extends StatelessWidget {
+  final Function onPressed;
+  final Widget icon;
+  final String text;
+  final TextStyle style;
+  final MainAxisSize mainAxisSize;
+
+  const IconArrowButton(this.icon, this.text, this.onPressed, {this.mainAxisSize, this.style});
+
+  @override
+  Widget build(BuildContext context) {
+    return InkWell(
+      onTap: onPressed,
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: <Widget>[
+          Divider(
+            color: ColorConstants.grayPrimary.withOpacity(0.15),
+            thickness: 1,
+            height: 1,
+          ),
+          Padding(
+            padding: const EdgeInsets.symmetric(horizontal: 16),
+            child: Row(
+              mainAxisSize: mainAxisSize ?? MainAxisSize.max,
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: <Widget>[
+                Padding(
+                  padding: EdgeInsets.symmetric(vertical: 12),
+                  child: Row(
+                    mainAxisAlignment: MainAxisAlignment.start,
+                    children: <Widget>[
+                      Padding(padding: EdgeInsets.only(right: 8), child: icon),
+                      Text(text ?? '',
+                          style: style ?? TextStyle(color: ColorConstants.grayPrimary)),
+                    ],
+                  ),
+                ),
+                Icon(
+                  Icons.arrow_forward_ios,
+                  color: ColorConstants.grayPrimary.withOpacity(0.3),
+                  size: 20,
+                )
+              ],
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/src/components/cached_image/cached_image_network.dart b/lib/src/components/cached_image/cached_image_network.dart
new file mode 100644
index 0000000000000000000000000000000000000000..30d504aa3b1c42fee6159dd6e0ef82a7f6d20bfd
--- /dev/null
+++ b/lib/src/components/cached_image/cached_image_network.dart
@@ -0,0 +1,37 @@
+import 'package:cached_network_image/cached_network_image.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/foundation.dart' show kIsWeb;
+
+class CustomCachedNetworkImage extends StatelessWidget {
+  final String imageUrl;
+
+  const CustomCachedNetworkImage({Key key, @required this.imageUrl}) :
+        assert(imageUrl != null), super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return kIsWeb ? Container(
+      decoration: BoxDecoration(
+          image: DecorationImage(
+              image: NetworkImage(imageUrl),
+              fit: BoxFit.scaleDown)),
+    ): CachedNetworkImage(
+      imageUrl: imageUrl,
+      imageBuilder: (context, imageProvider) => Container(
+        decoration: BoxDecoration(
+          borderRadius: BorderRadius.circular(20),
+            image: DecorationImage(
+                image: imageProvider, fit: BoxFit.contain)),
+      ),
+      placeholder: (context, _) =>
+      const Center(child: CircularProgressIndicator()),
+      errorWidget: (context, url, error) => Container(
+        decoration: BoxDecoration(
+            image: DecorationImage(
+                image: AssetImage(
+                    'assets/images/medical-item-placeholder.png'),
+                fit: BoxFit.scaleDown)),
+      ),
+    );
+  }
+}
diff --git a/lib/src/components/card_item_supplier/card_item_supplier.dart b/lib/src/components/card_item_supplier/card_item_supplier.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b3d860d19302acab936157bda9076644a0642193
--- /dev/null
+++ b/lib/src/components/card_item_supplier/card_item_supplier.dart
@@ -0,0 +1,109 @@
+import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/cached_image/cached_image_network.dart';
+
+class CardItemSupplier extends StatelessWidget {
+  final String itemName;
+  final int stock;
+  final String supplierName;
+  final String itemImage;
+  final int harga;
+
+  const CardItemSupplier({
+    Key key,
+    @required this.itemName,
+    @required this.stock,
+    @required this.supplierName,
+    @required this.itemImage,
+    @required this.harga,
+  })  : assert(itemName != null),
+        assert(stock != null),
+        assert(supplierName != null),
+        assert(itemImage != null),
+        assert(harga != null),
+        super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Card(
+        color: Colors.white,
+        child: Padding(
+          padding: EdgeInsets.symmetric(vertical: 12.0),
+          child: Row(
+            mainAxisAlignment: MainAxisAlignment.start,
+            children: <Widget>[
+              Expanded(
+                  flex: 3,
+                  child: Container(
+                      alignment: Alignment.center,
+                      padding: EdgeInsets.all(10),
+                      child: CustomCachedNetworkImage(imageUrl: itemImage))),
+              Expanded(
+                flex: 6,
+                child: Column(
+                  mainAxisAlignment: MainAxisAlignment.spaceAround,
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: <Widget>[
+                    Text(
+                      itemName,
+                      overflow: TextOverflow.ellipsis,
+                      maxLines: 1,
+                      style:
+                          TextStyle(fontSize: 17, fontWeight: FontWeight.w800),
+                    ),
+                    RichText(
+                      maxLines: 1,
+                      overflow: TextOverflow.ellipsis,
+                      text: TextSpan(
+                          style: TextStyle(
+                              fontSize: 17,
+                              color: ColorConstants.redSecondary,
+                              fontWeight: FontWeight.w700),
+                          children: <TextSpan>[
+                            TextSpan(
+                                text:
+                                    '${NumberFormat.currency(symbol: '', decimalDigits: 0).format(harga)}'),
+                            TextSpan(
+                                text: '/pcs',
+                                style: TextStyle(
+                                    fontSize: 15,
+                                    fontWeight: FontWeight.normal))
+                          ]),
+                    ),
+                    Expanded(child: Row(
+                      mainAxisAlignment: MainAxisAlignment.start,
+                      children: <Widget>[
+                        Padding(padding: EdgeInsets.all(4), child:
+                        Icon(
+                          Icons.storage,
+                          color: Colors.black,
+                        )),
+                        Text('Stok: $stock',
+                            overflow: TextOverflow.ellipsis,
+                            maxLines: 1,
+                            style: TextStyle(fontSize: 14)),
+                      ],
+                    )),
+                    Expanded(child: Row(
+                      mainAxisAlignment: MainAxisAlignment.start,
+                      children: <Widget>[
+                        Padding(padding: EdgeInsets.all(4), child:
+                        Icon(
+                          Icons.work,
+                          color: Colors.black,
+                        )),
+                        Text(supplierName,
+                            overflow: TextOverflow.ellipsis,
+                            maxLines: 1,
+                            style: TextStyle(fontSize: 14)),
+                      ],
+                    ))
+                  ],
+                ),
+              )
+            ],
+          ),
+        ));
+  }
+}
diff --git a/lib/src/components/cards/cards.dart b/lib/src/components/cards/cards.dart
new file mode 100644
index 0000000000000000000000000000000000000000..4218d25b1ac9a67a74dbe25eb90238397f6223f6
--- /dev/null
+++ b/lib/src/components/cards/cards.dart
@@ -0,0 +1,106 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+
+class GenericCard extends StatelessWidget {
+  final String head;
+  final String title;
+  final String subtitle;
+  final String body;
+  final EdgeInsetsGeometry titlePadding;
+
+  const GenericCard(
+      {this.head,
+      this.title,
+      this.subtitle,
+      this.body,
+      this.titlePadding,
+      Key key})
+      : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Card(
+        child: Padding(
+            padding: EdgeInsets.symmetric(horizontal: 16.0),
+            child: Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
+                mainAxisSize: MainAxisSize.min,
+                children: <Widget>[
+                  if (head != null)
+                    Padding(
+                        padding: EdgeInsetsDirectional.only(top: 16.0),
+                        child: Text(head,
+                            style: Theme.of(context).textTheme.subtitle2)),
+                  if (title != null)
+                    Padding(
+                        padding: titlePadding ?? EdgeInsets.only(top: 4.0),
+                        child: Text(title,
+                            style: Theme.of(context).textTheme.headline6)),
+                  if (subtitle != null)
+                    Padding(
+                        padding: titlePadding ?? EdgeInsets.only(top: 2.0),
+                        child: Text(subtitle,
+                            style: Theme.of(context).textTheme.subtitle2)),
+                  if (body != null)
+                    Padding(
+                      padding: EdgeInsets.only(top: 8.0, bottom: 16.0),
+                      child: Text(body,
+                          style: Theme.of(context).textTheme.subtitle1),
+                    ),
+                ])));
+  }
+}
+
+class StackOfCards extends StatelessWidget {
+  final int num;
+  final Widget child;
+  final double offset;
+  final bool vertical;
+  final bool horizontal;
+  final bool stretch;
+  final Color color;
+
+  const StackOfCards(
+      {Key key,
+      int num = 1,
+      @required this.child,
+      this.offset = 10.0,
+      this.vertical = false,
+      this.horizontal = true,
+      this.stretch = true,
+      this.color = Colors.white})
+      : num = num > 0 ? num : 1,
+        assert(offset != null),
+        super(key: key);
+
+  @override
+  Widget build(BuildContext context) => Column(
+          mainAxisSize: MainAxisSize.min,
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[
+            Stack(
+              children: List<Widget>.generate(
+                  num - 1,
+                  (val) => Positioned(
+                      bottom:
+                          vertical ? val * offset : val * offset - (num - 1),
+                      left:
+                          horizontal ? val * offset : val * offset - (num - 1),
+                      top: vertical && !stretch
+                          ? (num - val - 1) * offset
+                          : val * offset,
+                      right: horizontal && !stretch
+                          ? (num - val - 1) * offset
+                          : val * offset,
+                      child: Card(color: color, child: Container()))).toList()
+                ..add(
+                  Padding(
+                    child: child,
+                    padding: EdgeInsets.only(
+                        bottom: vertical ? (num - 1) * offset : 0,
+                        left: horizontal ? (num - 1) * offset : 0),
+                  ),
+                ),
+            )
+          ]);
+}
diff --git a/lib/src/components/input/input_search.dart b/lib/src/components/input/input_search.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e3dde5a622ba7c72d31f86003a84cb07ff49d3f7
--- /dev/null
+++ b/lib/src/components/input/input_search.dart
@@ -0,0 +1,34 @@
+import 'package:flutter/material.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+
+class InputSearch extends StatelessWidget {
+  final Function(String) onChange;
+
+  const InputSearch({
+    Key key, this.onChange,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return TextField(
+      onChanged: onChange,
+      decoration: InputDecoration(
+          contentPadding: EdgeInsets.symmetric(horizontal: 8),
+          labelStyle: TextStyle(color: Color(0xff4E7890)),
+          fillColor: Color(0xffF1F1F1),
+          filled: true,
+          border: OutlineInputBorder(),
+          enabledBorder: OutlineInputBorder(
+            borderSide: BorderSide(color: Color(0xff4E7890)),
+          ),
+          focusedBorder: OutlineInputBorder(
+            borderSide: BorderSide(color: ColorConstants.bluePrimary),
+          ),
+          suffixIcon: IconButton(
+            icon: Icon(Icons.search),
+            onPressed: () {},
+            color: ColorConstants.redSecondary,
+          )),
+    );
+  }
+}
diff --git a/lib/src/components/progressbar/progressbar.dart b/lib/src/components/progressbar/progressbar.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a1f426fa7f99778b235f6d712430120699bc9f10
--- /dev/null
+++ b/lib/src/components/progressbar/progressbar.dart
@@ -0,0 +1,47 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+
+class ProgressBar extends StatelessWidget {
+  final int current;
+  final int total;
+  final double height;
+  final double radius;
+  final Color currentColor;
+  final Color totalColor;
+
+  const ProgressBar(this.current, this.total,
+      {this.height = 20,
+      this.radius = 10,
+      this.currentColor = ColorConstants.redPrimary,
+      this.totalColor = ColorConstants.pinkPrimary,
+      Key key})
+      : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Stack(
+      children: <Widget>[
+        Container(
+          width: MediaQuery.of(context).size.width - 24,
+          height: height,
+          decoration: BoxDecoration(
+              color: totalColor,
+              borderRadius: BorderRadius.circular(radius)),
+        ),
+        Container(
+          height: height,
+          width:
+              _getProgress(context: context, available: current, total: total),
+          decoration: BoxDecoration(
+              color: currentColor,
+              borderRadius: BorderRadius.circular(radius)),
+        )
+      ],
+    );
+  }
+}
+
+double _getProgress({BuildContext context, int available, int total}) {
+  return MediaQuery.of(context).size.width * (available / total);
+}
diff --git a/lib/src/components/textform/textform.dart b/lib/src/components/textform/textform.dart
new file mode 100644
index 0000000000000000000000000000000000000000..849164f1bd59c18908ceaaafad94a38d4b09eeee
--- /dev/null
+++ b/lib/src/components/textform/textform.dart
@@ -0,0 +1,41 @@
+import 'package:flutter/material.dart';
+
+class TextFormInput extends StatelessWidget {
+  final String field;
+  final Function(String) onChange;
+  final TextInputType type;
+  final int line;
+
+  const TextFormInput({Key key, this.field, this.onChange, this.type, this.line}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Padding(
+      padding: const EdgeInsets.symmetric(vertical: 8.0),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: <Widget>[
+          Text(field),
+          TextFormField(
+            decoration: InputDecoration(
+              fillColor: Colors.white,
+              filled: true,
+              hintText: 'Tuliskan ${field.toLowerCase()} Anda di sini',
+              border: InputBorder.none
+            ),
+            keyboardType: type,
+            key: Key(field),
+            validator: (String val) {
+              if (val.isEmpty) {
+                return 'Data harus diisi.';
+              }
+              return null;
+            },
+            onChanged: onChange,
+            maxLines: line,
+          ),
+        ],
+      ),
+    );
+  }
+}
\ No newline at end of file
diff --git a/lib/src/models/dokumen.dart b/lib/src/models/dokumen.dart
new file mode 100644
index 0000000000000000000000000000000000000000..80426d0daf40794aebb4bd090a91f9b283ce4932
--- /dev/null
+++ b/lib/src/models/dokumen.dart
@@ -0,0 +1,28 @@
+import 'package:equatable/equatable.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'package:to_string/to_string.dart';
+
+part 'dokumen.g.dart';
+
+@JsonSerializable()
+@ToString()
+class Dokumen extends Equatable {
+  final String nama;
+  final String link;
+
+  Dokumen({
+    this.nama,
+    this.link,
+  });
+
+  factory Dokumen.fromJson(Map<String, dynamic> json) =>
+      _$DokumenFromJson(json);
+
+  Map<String, dynamic> toJson() => _$DokumenToJson(this);
+
+  @override
+  List<Object> get props => [nama, link];
+
+  @override
+  String toString() => _$DokumenToString(this);
+}
diff --git a/lib/src/models/dokumen.g.dart b/lib/src/models/dokumen.g.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c8d963cfdfc0bc720d847ea8d22cd415f16a3aad
--- /dev/null
+++ b/lib/src/models/dokumen.g.dart
@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'dokumen.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+Dokumen _$DokumenFromJson(Map<String, dynamic> json) {
+  return Dokumen(
+    nama: json['nama'] as String,
+    link: json['link'] as String,
+  );
+}
+
+Map<String, dynamic> _$DokumenToJson(Dokumen instance) => <String, dynamic>{
+      'nama': instance.nama,
+      'link': instance.link,
+    };
+
+// **************************************************************************
+// ToStringGenerator
+// **************************************************************************
+
+String _$DokumenToString(Dokumen o) {
+  return """Dokumen{nama: ${o.nama}, link: ${o.link}}""";
+}
diff --git a/lib/src/models/home_industry.dart b/lib/src/models/home_industry.dart
new file mode 100644
index 0000000000000000000000000000000000000000..190ab684e9aac887bef1aeec3a749700182c0b0c
--- /dev/null
+++ b/lib/src/models/home_industry.dart
@@ -0,0 +1,20 @@
+import 'package:json_annotation/json_annotation.dart';
+import 'package:mobile_apps/src/common/CustomSerializer.dart';
+import 'package:cloud_firestore/cloud_firestore.dart';
+part 'home_industry.g.dart';
+
+@JsonSerializable()
+class HomeIndustry {
+  final String id;
+  final String nama;
+  final String alamat;
+  @JsonKey(name: 'geolocation', fromJson: CustomSerializer.geoPointToMap)
+  final Map<String, double> geoLocation;
+
+  HomeIndustry({this.nama, this.alamat, this.id, this.geoLocation});
+
+  factory HomeIndustry.fromJson(Map<String, dynamic> json) =>
+      _$HomeIndustryFromJson(json);
+
+  Map<String, dynamic> toJson() => _$HomeIndustryToJson(this);
+}
diff --git a/lib/src/models/home_industry.g.dart b/lib/src/models/home_industry.g.dart
new file mode 100644
index 0000000000000000000000000000000000000000..cead521562f98e18a881b3b584713773d4d76487
--- /dev/null
+++ b/lib/src/models/home_industry.g.dart
@@ -0,0 +1,25 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'home_industry.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+HomeIndustry _$HomeIndustryFromJson(Map<String, dynamic> json) {
+  return HomeIndustry(
+    nama: json['nama'] as String,
+    alamat: json['alamat'] as String,
+    id: json['id'] as String,
+    geoLocation:
+        CustomSerializer.geoPointToMap(json['geolocation'] as GeoPoint),
+  );
+}
+
+Map<String, dynamic> _$HomeIndustryToJson(HomeIndustry instance) =>
+    <String, dynamic>{
+      'id': instance.id,
+      'nama': instance.nama,
+      'alamat': instance.alamat,
+      'geolocation': instance.geoLocation,
+    };
diff --git a/lib/src/models/institusi.dart b/lib/src/models/institusi.dart
new file mode 100644
index 0000000000000000000000000000000000000000..77754e11963833a98b47fc60f36daab2a18eb246
--- /dev/null
+++ b/lib/src/models/institusi.dart
@@ -0,0 +1,34 @@
+import 'package:equatable/equatable.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'package:to_string/to_string.dart';
+
+part 'institusi.g.dart';
+
+@JsonSerializable()
+@ToString()
+class Institusi extends Equatable {
+  final String nama;
+  final String alamat;
+  final String info1;
+  final String info2;
+  final String id;
+
+  Institusi({
+    this.nama,
+    this.alamat,
+    this.info1,
+    this.info2,
+    this.id,
+  });
+
+  factory Institusi.fromJson(Map<String, dynamic> json) =>
+      _$InstitusiFromJson(json);
+
+  Map<String, dynamic> toJson() => _$InstitusiToJson(this);
+
+  @override
+  List<Object> get props => [nama, alamat];
+
+  @override
+  String toString() => _$InstitusiToString(this);
+}
diff --git a/lib/src/models/institusi.g.dart b/lib/src/models/institusi.g.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d23d9f629459aceb86ff0587b9fcbcb50a94029f
--- /dev/null
+++ b/lib/src/models/institusi.g.dart
@@ -0,0 +1,33 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'institusi.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+Institusi _$InstitusiFromJson(Map<String, dynamic> json) {
+  return Institusi(
+    nama: json['nama'] as String,
+    alamat: json['alamat'] as String,
+    info1: json['info1'] as String,
+    info2: json['info2'] as String,
+    id: json['id'] as String,
+  );
+}
+
+Map<String, dynamic> _$InstitusiToJson(Institusi instance) => <String, dynamic>{
+      'nama': instance.nama,
+      'alamat': instance.alamat,
+      'info1': instance.info1,
+      'info2': instance.info2,
+      'id': instance.id,
+    };
+
+// **************************************************************************
+// ToStringGenerator
+// **************************************************************************
+
+String _$InstitusiToString(Institusi o) {
+  return """Institusi{nama: ${o.nama}, alamat: ${o.alamat}, info1: ${o.info1}, info2: ${o.info2}, id: ${o.id}}""";
+}
diff --git a/lib/src/models/inventory.dart b/lib/src/models/inventory.dart
index 847aa6ef7cf08f71241da48cc6fc645917f080d6..4cbffb1e9d3075cc1ae4a2bfce674d6e986013b7 100644
--- a/lib/src/models/inventory.dart
+++ b/lib/src/models/inventory.dart
@@ -7,16 +7,14 @@ part 'inventory.g.dart';
 @JsonSerializable()
 @ToString()
 class Inventory extends Equatable {
-  final String name;
-  final String imageUrl;
-  final int availableStock;
-  final int totalStock;
+  final String itemId;
+  final int kebutuhan;
+  final int terpenuhi;
 
   Inventory({
-    this.name,
-    this.imageUrl,
-    this.availableStock,
-    this.totalStock,
+  this.itemId,
+    this.kebutuhan,
+    this.terpenuhi
   });
 
   factory Inventory.fromJson(Map<String, dynamic> json) => _$InventoryFromJson(json);
@@ -24,7 +22,7 @@ class Inventory extends Equatable {
   Map<String, dynamic> toJson() => _$InventoryToJson(this);
 
   @override
-  List<Object> get props => [name, imageUrl];
+  List<Object> get props => [itemId];
 
   @override
   String toString() => _$InventoryToString(this);
diff --git a/lib/src/models/inventory.g.dart b/lib/src/models/inventory.g.dart
index 92cf8ccf9a255db83377de38d4a556be2573e4c1..29bbab1eb1551a7a2e7ed220ba092959a5c58160 100644
--- a/lib/src/models/inventory.g.dart
+++ b/lib/src/models/inventory.g.dart
@@ -8,18 +8,16 @@ part of 'inventory.dart';
 
 Inventory _$InventoryFromJson(Map<String, dynamic> json) {
   return Inventory(
-    name: json['name'] as String,
-    imageUrl: json['imageUrl'] as String,
-    availableStock: json['availableStock'] as int,
-    totalStock: json['totalStock'] as int,
+    itemId: json['itemId'] as String,
+    kebutuhan: json['kebutuhan'] as int,
+    terpenuhi: json['terpenuhi'] as int,
   );
 }
 
 Map<String, dynamic> _$InventoryToJson(Inventory instance) => <String, dynamic>{
-      'name': instance.name,
-      'imageUrl': instance.imageUrl,
-      'availableStock': instance.availableStock,
-      'totalStock': instance.totalStock,
+      'itemId': instance.itemId,
+      'kebutuhan': instance.kebutuhan,
+      'terpenuhi': instance.terpenuhi,
     };
 
 // **************************************************************************
@@ -27,5 +25,5 @@ Map<String, dynamic> _$InventoryToJson(Inventory instance) => <String, dynamic>{
 // **************************************************************************
 
 String _$InventoryToString(Inventory o) {
-  return '''Inventory{name: ${o.name}, imageUrl: ${o.imageUrl}, availableStock: ${o.availableStock}, totalStock: ${o.totalStock}}''';
+  return """Inventory{itemId: ${o.itemId}, kebutuhan: ${o.kebutuhan}, terpenuhi: ${o.terpenuhi}}""";
 }
diff --git a/lib/src/models/item.dart b/lib/src/models/item.dart
new file mode 100644
index 0000000000000000000000000000000000000000..1c613405954d14ecf4e0368767c3f58b36a5c1b6
--- /dev/null
+++ b/lib/src/models/item.dart
@@ -0,0 +1,27 @@
+import 'package:equatable/equatable.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'package:to_string/to_string.dart';
+
+part 'item.g.dart';
+
+@JsonSerializable()
+@ToString()
+class Item extends Equatable {
+  final String deskripsi;
+  final String manfaat;
+  final String nama;
+  final String url;
+
+  Item({this.deskripsi, this.manfaat, this.nama, this.url});
+
+  factory Item.fromJson(Map<String, dynamic> json) =>
+      _$ItemFromJson(json);
+
+  Map<String, dynamic> toJson() => _$ItemToJson(this);
+
+  @override
+  List<Object> get props => [nama, url];
+
+  @override
+  String toString() => _$ItemToString(this);
+}
\ No newline at end of file
diff --git a/lib/src/models/item.g.dart b/lib/src/models/item.g.dart
new file mode 100644
index 0000000000000000000000000000000000000000..4851c66dd185a9a94a666ae66cfd41573dfb266c
--- /dev/null
+++ b/lib/src/models/item.g.dart
@@ -0,0 +1,31 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'item.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+Item _$ItemFromJson(Map<String, dynamic> json) {
+  return Item(
+    deskripsi: json['deskripsi'] as String,
+    manfaat: json['manfaat'] as String,
+    nama: json['nama'] as String,
+    url: json['url'] as String,
+  );
+}
+
+Map<String, dynamic> _$ItemToJson(Item instance) => <String, dynamic>{
+      'deskripsi': instance.deskripsi,
+      'manfaat': instance.manfaat,
+      'nama': instance.nama,
+      'url': instance.url,
+    };
+
+// **************************************************************************
+// ToStringGenerator
+// **************************************************************************
+
+String _$ItemToString(Item o) {
+  return """Item{deskripsi: ${o.deskripsi}, manfaat: ${o.manfaat}, nama: ${o.nama}, url: ${o.url}}""";
+}
diff --git a/lib/src/models/item_needed.dart b/lib/src/models/item_needed.dart
new file mode 100644
index 0000000000000000000000000000000000000000..996383430dd9b9f0336a70379b684fb4a5df0ba6
--- /dev/null
+++ b/lib/src/models/item_needed.dart
@@ -0,0 +1,37 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'package:mobile_apps/src/common/CustomSerializer.dart';
+part 'item_needed.g.dart';
+
+@JsonSerializable(nullable: false)
+class ItemNeeded {
+  final String id;
+  final int kebutuhan;
+
+  @JsonKey(name: 'item', fromJson: CustomSerializer.docReferenceToPath)
+  final String itemPath;
+
+  @JsonKey(
+      name: 'periode_berakhir', fromJson: CustomSerializer.timeStampToDateTime)
+  final DateTime periodeTerakhir;
+
+  @JsonKey(
+      name: 'periode_mulai', fromJson: CustomSerializer.timeStampToDateTime)
+  final DateTime periodeMulai;
+
+  final int terpenuhi;
+
+  const ItemNeeded({
+    this.id,
+    this.kebutuhan,
+    this.periodeTerakhir,
+    this.periodeMulai,
+    this.terpenuhi,
+    this.itemPath,
+  });
+
+  factory ItemNeeded.fromJson(Map<String, dynamic> json) =>
+      _$ItemNeededFromJson(json);
+
+  Map<String, dynamic> toJson() => _$ItemNeededToJson(this);
+}
diff --git a/lib/src/models/item_needed.g.dart b/lib/src/models/item_needed.g.dart
new file mode 100644
index 0000000000000000000000000000000000000000..ec2c708adedaaf783aab632396c578657b94c818
--- /dev/null
+++ b/lib/src/models/item_needed.g.dart
@@ -0,0 +1,31 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'item_needed.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ItemNeeded _$ItemNeededFromJson(Map<String, dynamic> json) {
+  return ItemNeeded(
+    id: json['id'] as String,
+    kebutuhan: json['kebutuhan'] as int,
+    periodeTerakhir: CustomSerializer.timeStampToDateTime(
+        json['periode_berakhir'] as Timestamp),
+    periodeMulai: CustomSerializer.timeStampToDateTime(
+        json['periode_mulai'] as Timestamp),
+    terpenuhi: json['terpenuhi'] as int,
+    itemPath:
+        CustomSerializer.docReferenceToPath(json['item'] as DocumentReference),
+  );
+}
+
+Map<String, dynamic> _$ItemNeededToJson(ItemNeeded instance) =>
+    <String, dynamic>{
+      'id': instance.id,
+      'kebutuhan': instance.kebutuhan,
+      'item': instance.itemPath,
+      'periode_berakhir': instance.periodeTerakhir.toIso8601String(),
+      'periode_mulai': instance.periodeMulai.toIso8601String(),
+      'terpenuhi': instance.terpenuhi,
+    };
diff --git a/lib/src/models/item_supplier_aggregate.dart b/lib/src/models/item_supplier_aggregate.dart
new file mode 100644
index 0000000000000000000000000000000000000000..530eceed9dcc51a7cbe1264cbdd6bdcb00c574b1
--- /dev/null
+++ b/lib/src/models/item_supplier_aggregate.dart
@@ -0,0 +1,23 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'package:mobile_apps/src/common/CustomSerializer.dart';
+part 'item_supplier_aggregate.g.dart';
+
+@JsonSerializable(nullable: false)
+class ItemSupplierAggregate {
+
+  @JsonKey(name: 'barang', fromJson: CustomSerializer.docReferenceToPath)
+  final String barangPath;
+
+  @JsonKey(name: 'supplier', fromJson: CustomSerializer.docReferenceToPath)
+  final String supplierPath;
+
+  const ItemSupplierAggregate({
+    this.barangPath, this.supplierPath,
+  });
+
+  factory ItemSupplierAggregate.fromJson(Map<String, dynamic> json) =>
+      _$ItemSupplierAggregateFromJson(json);
+
+  Map<String, dynamic> toJson() => _$ItemSupplierAggregateToJson(this);
+}
\ No newline at end of file
diff --git a/lib/src/models/item_supplier_aggregate.g.dart b/lib/src/models/item_supplier_aggregate.g.dart
new file mode 100644
index 0000000000000000000000000000000000000000..eb35dd5a8f28d82844c5bbcb51820e82abcdeb75
--- /dev/null
+++ b/lib/src/models/item_supplier_aggregate.g.dart
@@ -0,0 +1,24 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'item_supplier_aggregate.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ItemSupplierAggregate _$ItemSupplierAggregateFromJson(
+    Map<String, dynamic> json) {
+  return ItemSupplierAggregate(
+    barangPath: CustomSerializer.docReferenceToPath(
+        json['barang'] as DocumentReference),
+    supplierPath: CustomSerializer.docReferenceToPath(
+        json['supplier'] as DocumentReference),
+  );
+}
+
+Map<String, dynamic> _$ItemSupplierAggregateToJson(
+        ItemSupplierAggregate instance) =>
+    <String, dynamic>{
+      'barang': instance.barangPath,
+      'supplier': instance.supplierPath,
+    };
diff --git a/lib/src/models/kontak.dart b/lib/src/models/kontak.dart
new file mode 100644
index 0000000000000000000000000000000000000000..ab3c2fcd50e8462b1498ff890e4ae014c5270fff
--- /dev/null
+++ b/lib/src/models/kontak.dart
@@ -0,0 +1,32 @@
+import 'package:equatable/equatable.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'package:to_string/to_string.dart';
+
+part 'kontak.g.dart';
+
+@JsonSerializable()
+@ToString()
+class Kontak extends Equatable {
+  final String email;
+  final String jabatan;
+  final String nama;
+  final String telepon;
+
+  Kontak({
+    this.email,
+    this.jabatan,
+    this.nama,
+    this.telepon,
+  });
+
+  factory Kontak.fromJson(Map<String, dynamic> json) =>
+      _$KontakFromJson(json);
+
+  Map<String, dynamic> toJson() => _$KontakToJson(this);
+
+  @override
+  List<Object> get props => [nama, telepon];
+
+  @override
+  String toString() => _$KontakToString(this);
+}
diff --git a/lib/src/models/kontak.g.dart b/lib/src/models/kontak.g.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c29b9c3a454ee068bc01d57047d8f631108a6b85
--- /dev/null
+++ b/lib/src/models/kontak.g.dart
@@ -0,0 +1,31 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'kontak.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+Kontak _$KontakFromJson(Map<String, dynamic> json) {
+  return Kontak(
+    email: json['email'] as String,
+    jabatan: json['jabatan'] as String,
+    nama: json['nama'] as String,
+    telepon: json['telepon'] as String,
+  );
+}
+
+Map<String, dynamic> _$KontakToJson(Kontak instance) => <String, dynamic>{
+      'email': instance.email,
+      'jabatan': instance.jabatan,
+      'nama': instance.nama,
+      'telepon': instance.telepon,
+    };
+
+// **************************************************************************
+// ToStringGenerator
+// **************************************************************************
+
+String _$KontakToString(Kontak o) {
+  return """Kontak{email: ${o.email}, jabatan: ${o.jabatan}, nama: ${o.nama}, telepon: ${o.telepon}}""";
+}
diff --git a/lib/src/models/notifikasi.dart b/lib/src/models/notifikasi.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a299afc868da1a344412b19d16690e8c0da6def5
--- /dev/null
+++ b/lib/src/models/notifikasi.dart
@@ -0,0 +1,49 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:equatable/equatable.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'package:to_string/to_string.dart';
+
+part 'notifikasi.g.dart';
+
+@JsonSerializable()
+@ToString()
+class Notifikasi extends Equatable {
+  @JsonKey(
+    fromJson: _timestampToDateTime,
+  )
+  final DateTime waktu;
+  final String judul;
+  final String deskripsi;
+  final String tipe;
+  @JsonKey(
+    fromJson: _docRef,
+    toJson: _docRef,
+  )
+  final DocumentReference objectPath;
+
+  Notifikasi({
+    this.waktu,
+    this.judul,
+    this.deskripsi,
+    this.tipe,
+    this.objectPath,
+  });
+
+  factory Notifikasi.fromJson(Map<String, dynamic> json) => _$NotifikasiFromJson(json);
+
+  Map<String, dynamic> toJson() => _$NotifikasiToJson(this);
+
+  @override
+  List<Object> get props => [waktu, judul, tipe, objectPath,];
+
+  @override
+  String toString() => _$NotifikasiToString(this);
+
+  static DateTime _timestampToDateTime(Timestamp waktu) {
+    return DateTime.fromMicrosecondsSinceEpoch(waktu.microsecondsSinceEpoch);
+  }
+
+  static DocumentReference _docRef(DocumentReference objectPath) {
+    return objectPath;
+  }
+}
diff --git a/lib/src/models/notifikasi.g.dart b/lib/src/models/notifikasi.g.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d3780a7c3ba6203b2be1067223a6923895563f43
--- /dev/null
+++ b/lib/src/models/notifikasi.g.dart
@@ -0,0 +1,34 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'notifikasi.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+Notifikasi _$NotifikasiFromJson(Map<String, dynamic> json) {
+  return Notifikasi(
+    waktu: Notifikasi._timestampToDateTime(json['waktu'] as Timestamp),
+    judul: json['judul'] as String,
+    deskripsi: json['deskripsi'] as String,
+    tipe: json['tipe'] as String,
+    objectPath: Notifikasi._docRef(json['objectPath'] as DocumentReference),
+  );
+}
+
+Map<String, dynamic> _$NotifikasiToJson(Notifikasi instance) =>
+    <String, dynamic>{
+      'waktu': instance.waktu?.toIso8601String(),
+      'judul': instance.judul,
+      'deskripsi': instance.deskripsi,
+      'tipe': instance.tipe,
+      'objectPath': Notifikasi._docRef(instance.objectPath),
+    };
+
+// **************************************************************************
+// ToStringGenerator
+// **************************************************************************
+
+String _$NotifikasiToString(Notifikasi o) {
+  return """Notifikasi{waktu: ${o.waktu}, judul: ${o.judul}, deskripsi: ${o.deskripsi}, tipe: ${o.tipe}, objectPath: ${o.objectPath}}""";
+}
diff --git a/lib/src/models/product_home_industry.dart b/lib/src/models/product_home_industry.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6c7137463279857469ed3b25cc0429fb4150fdd2
--- /dev/null
+++ b/lib/src/models/product_home_industry.dart
@@ -0,0 +1,20 @@
+import 'package:json_annotation/json_annotation.dart';
+import 'package:mobile_apps/src/common/CustomSerializer.dart';
+import 'package:cloud_firestore/cloud_firestore.dart';
+part 'product_home_industry.g.dart';
+@JsonSerializable()
+class ProductHomeIndustry {
+  @JsonKey(name: 'nama', fromJson: CustomSerializer.docReferenceToPath)
+  final String idItem;
+  final String bahan;
+  final String durasi;
+  final int kapasitas;
+  final String id;
+  final int harga;
+
+  ProductHomeIndustry({this.idItem, this.bahan, this.durasi, this.kapasitas, this.id, this.harga});
+  factory ProductHomeIndustry.fromJson(Map<String, dynamic> json) =>
+      _$ProductHomeIndustryFromJson(json);
+
+  Map<String, dynamic> toJson() => _$ProductHomeIndustryToJson(this);
+}
\ No newline at end of file
diff --git a/lib/src/models/product_home_industry.g.dart b/lib/src/models/product_home_industry.g.dart
new file mode 100644
index 0000000000000000000000000000000000000000..bafb3b155b936e4ffacfc7eb75ffd140fa0de5b4
--- /dev/null
+++ b/lib/src/models/product_home_industry.g.dart
@@ -0,0 +1,30 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'product_home_industry.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ProductHomeIndustry _$ProductHomeIndustryFromJson(Map<String, dynamic> json) {
+  return ProductHomeIndustry(
+    idItem:
+        CustomSerializer.docReferenceToPath(json['nama'] as DocumentReference),
+    bahan: json['bahan'] as String,
+    durasi: json['durasi'] as String,
+    kapasitas: json['kapasitas'] as int,
+    id: json['id'] as String,
+    harga: json['harga'] as int,
+  );
+}
+
+Map<String, dynamic> _$ProductHomeIndustryToJson(
+        ProductHomeIndustry instance) =>
+    <String, dynamic>{
+      'nama': instance.idItem,
+      'bahan': instance.bahan,
+      'durasi': instance.durasi,
+      'kapasitas': instance.kapasitas,
+      'id': instance.id,
+      'harga': instance.harga,
+    };
diff --git a/lib/src/models/product_supplier.dart b/lib/src/models/product_supplier.dart
new file mode 100644
index 0000000000000000000000000000000000000000..034facc26aba48f9bb999e1756b5db98df0c4f82
--- /dev/null
+++ b/lib/src/models/product_supplier.dart
@@ -0,0 +1,26 @@
+import 'package:json_annotation/json_annotation.dart';
+import 'package:mobile_apps/src/common/CustomSerializer.dart';
+import 'package:cloud_firestore/cloud_firestore.dart';
+part 'product_supplier.g.dart';
+
+@JsonSerializable()
+class ProductSupplier {
+  @JsonKey(name: 'item', fromJson: CustomSerializer.docReferenceToPath)
+  final String idItem;
+  final String admin;
+  final String spesifikasi;
+  final int stok;
+  final String manfaat;
+  final String id;
+  final int harga;
+
+  @JsonKey(fromJson: CustomSerializer.timeStampToDateTime)
+  final DateTime timestamp;
+
+  ProductSupplier({this.id, this.idItem, this.admin, this.spesifikasi, this.stok, this.timestamp, this.manfaat, this.harga});
+
+  factory ProductSupplier.fromJson(Map<String, dynamic> json) =>
+      _$ProductSupplierFromJson(json);
+
+  Map<String, dynamic> toJson() => _$ProductSupplierToJson(this);
+}
\ No newline at end of file
diff --git a/lib/src/models/product_supplier.g.dart b/lib/src/models/product_supplier.g.dart
new file mode 100644
index 0000000000000000000000000000000000000000..08457a4463431260331da423a3807a957996e53c
--- /dev/null
+++ b/lib/src/models/product_supplier.g.dart
@@ -0,0 +1,34 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'product_supplier.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ProductSupplier _$ProductSupplierFromJson(Map<String, dynamic> json) {
+  return ProductSupplier(
+    id: json['id'] as String,
+    idItem:
+        CustomSerializer.docReferenceToPath(json['item'] as DocumentReference),
+    admin: json['admin'] as String,
+    spesifikasi: json['spesifikasi'] as String,
+    stok: json['stok'] as int,
+    timestamp:
+        CustomSerializer.timeStampToDateTime(json['timestamp'] as Timestamp),
+    manfaat: json['manfaat'] as String,
+    harga: json['harga'] as int,
+  );
+}
+
+Map<String, dynamic> _$ProductSupplierToJson(ProductSupplier instance) =>
+    <String, dynamic>{
+      'item': instance.idItem,
+      'admin': instance.admin,
+      'spesifikasi': instance.spesifikasi,
+      'stok': instance.stok,
+      'manfaat': instance.manfaat,
+      'id': instance.id,
+      'harga': instance.harga,
+      'timestamp': instance.timestamp?.toIso8601String(),
+    };
diff --git a/lib/src/models/supplier.dart b/lib/src/models/supplier.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c403f129a924405c6fe325b3b996b113e4526a34
--- /dev/null
+++ b/lib/src/models/supplier.dart
@@ -0,0 +1,34 @@
+import 'package:json_annotation/json_annotation.dart';
+import 'package:mobile_apps/src/common/CustomSerializer.dart';
+import 'package:cloud_firestore/cloud_firestore.dart';
+
+part 'supplier.g.dart';
+
+@JsonSerializable()
+class Supplier {
+  final String id;
+  final String alamat;
+  final String nama;
+  final String rekomendasi;
+  final String sales;
+  final String telepon;
+  final String url;
+
+  @JsonKey(name: 'geolocation', fromJson: CustomSerializer.geoPointToMap)
+  final Map<String,double> geoLocation;
+  Supplier({
+    this.id,
+    this.nama,
+    this.url,
+    this.alamat,
+    this.rekomendasi,
+    this.sales,
+    this.telepon,
+    this.geoLocation,
+  });
+
+  factory Supplier.fromJson(Map<String, dynamic> json) =>
+      _$SupplierFromJson(json);
+
+  Map<String, dynamic> toJson() => _$SupplierToJson(this);
+}
diff --git a/lib/src/models/supplier.g.dart b/lib/src/models/supplier.g.dart
new file mode 100644
index 0000000000000000000000000000000000000000..943a2be9af2214dd03f5c6f01a0e230392df5e1c
--- /dev/null
+++ b/lib/src/models/supplier.g.dart
@@ -0,0 +1,32 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'supplier.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+Supplier _$SupplierFromJson(Map<String, dynamic> json) {
+  return Supplier(
+    id: json['id'] as String,
+    nama: json['nama'] as String,
+    url: json['url'] as String,
+    alamat: json['alamat'] as String,
+    rekomendasi: json['rekomendasi'] as String,
+    sales: json['sales'] as String,
+    telepon: json['telepon'] as String,
+    geoLocation:
+        CustomSerializer.geoPointToMap(json['geolocation'] as GeoPoint),
+  );
+}
+
+Map<String, dynamic> _$SupplierToJson(Supplier instance) => <String, dynamic>{
+      'id': instance.id,
+      'alamat': instance.alamat,
+      'nama': instance.nama,
+      'rekomendasi': instance.rekomendasi,
+      'sales': instance.sales,
+      'telepon': instance.telepon,
+      'url': instance.url,
+      'geolocation': instance.geoLocation,
+    };
diff --git a/lib/src/models/ulasan.dart b/lib/src/models/ulasan.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a936b749f44aee1b1e0017081052c8e6ed29abcb
--- /dev/null
+++ b/lib/src/models/ulasan.dart
@@ -0,0 +1,53 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:equatable/equatable.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'package:to_string/to_string.dart';
+import 'package:intl/intl.dart';
+
+part 'ulasan.g.dart';
+
+@JsonSerializable()
+@ToString()
+class Ulasan extends Equatable {
+  final String email;
+  final String jabatan;
+  final String nama;
+  final String telepon;
+
+  final String body;
+  final DateTime timestamp;
+
+  static final DateFormat _dateFormat = DateFormat('dd MMMM yyyy');
+  String get displayDate {
+    return _dateFormat.format(timestamp);
+  }
+
+  Ulasan({
+    this.email,
+    this.jabatan,
+    this.nama,
+    this.telepon,
+    this.body,
+    this.timestamp,
+  });
+
+  factory Ulasan.fromJson(Map<String, dynamic> json) {
+    // modification to parse firebase Timestamp to DateTime
+    // without changing generated code from json_serializable
+    Timestamp timestamp = json['timestamp'];
+    if (timestamp != null) {
+      json['timestamp'] = DateTime.fromMicrosecondsSinceEpoch(timestamp.microsecondsSinceEpoch).toIso8601String();
+    }
+
+    return _$UlasanFromJson(json);
+  }
+
+
+  Map<String, dynamic> toJson() => _$UlasanToJson(this);
+
+  @override
+  List<Object> get props => [nama, body];
+
+  @override
+  String toString() => _$UlasanToString(this);
+}
diff --git a/lib/src/models/ulasan.g.dart b/lib/src/models/ulasan.g.dart
new file mode 100644
index 0000000000000000000000000000000000000000..581fb1b4e8700cc7bb984551f730a5afdbde9bc3
--- /dev/null
+++ b/lib/src/models/ulasan.g.dart
@@ -0,0 +1,37 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'ulasan.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+Ulasan _$UlasanFromJson(Map<String, dynamic> json) {
+  return Ulasan(
+    email: json['email'] as String,
+    jabatan: json['jabatan'] as String,
+    nama: json['nama'] as String,
+    telepon: json['telepon'] as String,
+    body: json['body'] as String,
+    timestamp: json['timestamp'] == null
+        ? null
+        : DateTime.parse(json['timestamp'] as String),
+  );
+}
+
+Map<String, dynamic> _$UlasanToJson(Ulasan instance) => <String, dynamic>{
+      'email': instance.email,
+      'jabatan': instance.jabatan,
+      'nama': instance.nama,
+      'telepon': instance.telepon,
+      'body': instance.body,
+      'timestamp': instance.timestamp?.toIso8601String(),
+    };
+
+// **************************************************************************
+// ToStringGenerator
+// **************************************************************************
+
+String _$UlasanToString(Ulasan o) {
+  return """Ulasan{email: ${o.email}, jabatan: ${o.jabatan}, nama: ${o.nama}, telepon: ${o.telepon}, body: ${o.body}, timestamp: ${o.timestamp}}""";
+}
diff --git a/lib/src/repositories/dokumen_repository.dart b/lib/src/repositories/dokumen_repository.dart
new file mode 100644
index 0000000000000000000000000000000000000000..89d7b13256d42151d991a8e5945ec912fd58f328
--- /dev/null
+++ b/lib/src/repositories/dokumen_repository.dart
@@ -0,0 +1,18 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/models/dokumen.dart';
+
+class DokumenRepository {
+  List<Dokumen> allDokumen(AsyncSnapshot<QuerySnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    return snapshot.data.documents
+        .map<Dokumen>((document) => Dokumen.fromJson(document.data))
+        .toList();
+  }
+
+  CollectionReference dokumenCollections(String idSupplier) =>
+      Firestore.instance.collection('waspada/covid19/supplier/$idSupplier/dokumen');
+
+  Stream<QuerySnapshot> dokumenSnapshots(String idSupplier) =>
+      dokumenCollections(idSupplier).snapshots();
+}
diff --git a/lib/src/repositories/home_industry_repository.dart b/lib/src/repositories/home_industry_repository.dart
new file mode 100644
index 0000000000000000000000000000000000000000..641dcb1dd30c74a10df7c44d018af68a1eddc105
--- /dev/null
+++ b/lib/src/repositories/home_industry_repository.dart
@@ -0,0 +1,57 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/models/home_industry.dart';
+import 'package:mobile_apps/src/models/kontak.dart';
+import 'package:mobile_apps/src/models/product_home_industry.dart';
+
+class HomeIndustryRepository {
+  CollectionReference get homeIndustryCollections =>
+      Firestore.instance.collection('waspada/covid19/home-industry');
+
+  DocumentReference getHomeIndustryDocument(String id) {
+    return Firestore.instance.document('waspada/covid19/home-industry/$id');
+  }
+
+  CollectionReference getAllKontakHomeIndustry(String id) {
+    return Firestore.instance
+        .collection('waspada/covid19/home-industry/$id/kontak');
+  }
+
+  CollectionReference getAllProductHomeIndustry(String id) {
+    return Firestore.instance
+        .collection('waspada/covid19/home-industry/$id/barang');
+  }
+
+  Stream<QuerySnapshot> get homeIndustrySnapshots =>
+      homeIndustryCollections.snapshots();
+
+  List<HomeIndustry> semuaHomeIndustry(AsyncSnapshot<QuerySnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    return snapshot.data.documents
+        .map<HomeIndustry>((document) => HomeIndustry.fromJson(
+            document.data..addAll({'id': document.documentID})))
+        .toList();
+  }
+
+  HomeIndustry homeIndustry(AsyncSnapshot<DocumentSnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return null;
+    return HomeIndustry.fromJson(
+        snapshot.data.data..addAll({'id': snapshot.data.documentID}));
+  }
+
+  List<Kontak> semuaKontakHomeIndustry(AsyncSnapshot<QuerySnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    return snapshot.data.documents
+        .map<Kontak>((document) => Kontak.fromJson(document.data))
+        .toList();
+  }
+
+  List<ProductHomeIndustry> semuaProductHomeIndustry(
+      AsyncSnapshot<QuerySnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    return snapshot.data.documents
+        .map<ProductHomeIndustry>((document) => ProductHomeIndustry.fromJson(
+            document.data..addAll({'id': document.documentID})))
+        .toList();
+  }
+}
diff --git a/lib/src/repositories/institusi_repository.dart b/lib/src/repositories/institusi_repository.dart
new file mode 100644
index 0000000000000000000000000000000000000000..cc6ac835b9d5519a885e97e7b1bde0fb4bdc4498
--- /dev/null
+++ b/lib/src/repositories/institusi_repository.dart
@@ -0,0 +1,27 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/models/institusi.dart';
+
+class InstitusiRepository {
+  CollectionReference get institusiCollections =>
+      Firestore.instance.collection('waspada/covid19/institusi');
+
+  DocumentReference institusiDocument(String id) => Firestore.instance.document('waspada/covid19/institusi/$id');
+
+  Stream<QuerySnapshot> get institusiSnapshots =>
+      institusiCollections.snapshots();
+
+  List<Institusi> semuaInstitusi(AsyncSnapshot<QuerySnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    return snapshot.data.documents
+        .map<Institusi>((document) => Institusi.fromJson(
+            document.data..addAll({'id': document.documentID})))
+        .toList();
+  }
+
+  Institusi institusi(AsyncSnapshot<DocumentSnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return null;
+    return Institusi.fromJson(
+        snapshot.data.data..addAll({'id': snapshot.data.documentID}));
+  }
+}
diff --git a/lib/src/repositories/inventory_repository.dart b/lib/src/repositories/inventory_repository.dart
index 58a5c04e8bfc7ec1685e7f012b626f950033b0c2..f01732b93d0b25ff5964703ecec3b4907792adea 100644
--- a/lib/src/repositories/inventory_repository.dart
+++ b/lib/src/repositories/inventory_repository.dart
@@ -3,12 +3,18 @@ import 'package:flutter/widgets.dart';
 import 'package:mobile_apps/src/models/inventory.dart';
 
 class InventoryRepository {
-  CollectionReference get inventoriesCollections => Firestore.instance.collection('inventories');
+  CollectionReference get inventoriesCollections => Firestore.instance.collection('waspada/covid19/agregasi');
 
   Stream<QuerySnapshot> get inventoriesSnapshots => inventoriesCollections.snapshots();
 
   List<Inventory> inventories(AsyncSnapshot<QuerySnapshot> snapshot) {
+    Map<String, dynamic> inventory;
     if (!snapshot.hasData || snapshot.hasError) return [];
-    return snapshot.data.documents.map<Inventory>((document) => Inventory.fromJson(document.data)).toList();
+    return snapshot.data.documents.map<Inventory>((document) {
+      inventory = {...document.data};
+      inventory['itemId'] = inventory['item'].documentID;
+      inventory.remove('item');
+      return Inventory.fromJson(inventory);
+    }).toList();
   }
 }
diff --git a/lib/src/repositories/item_ketersediaan_aggregate_repository.dart b/lib/src/repositories/item_ketersediaan_aggregate_repository.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a9c7c69d18f0b148b59c80156a513bee54c76204
--- /dev/null
+++ b/lib/src/repositories/item_ketersediaan_aggregate_repository.dart
@@ -0,0 +1,34 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/material.dart';
+import 'package:mobile_apps/src/models/item_supplier_aggregate.dart';
+
+class ItemKetersediaanRepository {
+  CollectionReference get itemCollections =>
+      Firestore.instance.collection('waspada/covid19/agregrasi-ketersediaan');
+
+  List<ItemSupplierAggregate> allItemAvailable(
+      AsyncSnapshot<QuerySnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    return snapshot.data.documents
+        .map<ItemSupplierAggregate>(
+            (document) => ItemSupplierAggregate.fromJson(document.data))
+        .toList();
+  }
+
+  List<ItemSupplierAggregate> allItemSearch(
+      String search, AsyncSnapshot<QuerySnapshot> snapshot) {
+    final searchLower = search.toLowerCase();
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    return snapshot.data.documents
+        .where((element) =>
+            (element.data['nama_item'] as String)
+                .toLowerCase()
+                .contains(searchLower) ||
+            (element.data['nama_supplier'] as String)
+                .toLowerCase()
+                .contains(searchLower))
+        .map<ItemSupplierAggregate>(
+            (document) => ItemSupplierAggregate.fromJson(document.data))
+        .toList();
+  }
+}
diff --git a/lib/src/repositories/item_needed_repositories.dart b/lib/src/repositories/item_needed_repositories.dart
new file mode 100644
index 0000000000000000000000000000000000000000..3d634d5fdd8d37ecfbd5b538563cfb2aab82f407
--- /dev/null
+++ b/lib/src/repositories/item_needed_repositories.dart
@@ -0,0 +1,20 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/material.dart';
+import 'package:mobile_apps/src/models/item_needed.dart';
+
+class ItemNeededRepository {
+  final String idInstitusi;
+  final String idItemNeeded;
+  const ItemNeededRepository(
+      {@required this.idInstitusi, @required this.idItemNeeded});
+  DocumentReference get itemNeededDocument => Firestore.instance.document(
+      'waspada/covid19/institusi/${idInstitusi}/kebutuhan/${idItemNeeded}');
+
+  ItemNeeded itemNeeded(AsyncSnapshot<DocumentSnapshot> snapshot) {
+    Map<String, dynamic> itemNeeded;
+    if (!snapshot.hasData || snapshot.hasError) return null;
+    itemNeeded = snapshot.data.data;
+    itemNeeded['id'] = snapshot.data.documentID;
+    return ItemNeeded.fromJson(itemNeeded);
+  }
+}
diff --git a/lib/src/repositories/item_repository.dart b/lib/src/repositories/item_repository.dart
new file mode 100644
index 0000000000000000000000000000000000000000..7bb0b8e86f5487687b3afcc91dded9e1643fe4d9
--- /dev/null
+++ b/lib/src/repositories/item_repository.dart
@@ -0,0 +1,27 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/models/item.dart';
+
+class ItemRepository {
+  CollectionReference get itemCollections => Firestore.instance.collection('waspada/covid19/items');
+
+  DocumentReference itemDetail(String id) {
+    return Firestore.instance.document('waspada/covid19/items/$id');
+  }
+
+  DocumentReference itemDocumentFromPath({@required path}) =>
+      Firestore.instance.document(path);
+
+  Stream<QuerySnapshot> get itemSnapshots => itemCollections.snapshots();
+
+  List<Item> inventories(AsyncSnapshot<QuerySnapshot> snapshot) {
+    return snapshot.data.documents
+        .map<Item>((document) => Item.fromJson(document.data))
+        .toList();
+  }
+
+  Item item(AsyncSnapshot<DocumentSnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return null;
+    return Item.fromJson(snapshot.data.data);
+  }
+}
diff --git a/lib/src/repositories/kebutuhan_repository.dart b/lib/src/repositories/kebutuhan_repository.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d424891354ab038d29912a406be0d28d8aa9b52b
--- /dev/null
+++ b/lib/src/repositories/kebutuhan_repository.dart
@@ -0,0 +1,23 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/material.dart';
+import 'package:mobile_apps/src/models/item_needed.dart';
+
+class KebutuhanRepository {
+  CollectionReference kebutuhanCollections(String institusiId) =>
+      Firestore.instance.collection(
+          'waspada/covid19/institusi/${institusiId}/kebutuhan');
+
+  Stream<QuerySnapshot> kebutuhanSnapshots(String institusiId) =>
+      kebutuhanCollections(institusiId).snapshots();
+
+  List<ItemNeeded> kebutuhan(AsyncSnapshot<QuerySnapshot> snapshot) {
+    Map<String, dynamic> itemNeeded;
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    return snapshot.data.documents
+        .map<ItemNeeded>((document) {
+          itemNeeded = document.data;
+          itemNeeded['id'] = document.documentID;
+          return ItemNeeded.fromJson(itemNeeded);})
+        .toList();
+  }
+}
\ No newline at end of file
diff --git a/lib/src/repositories/kontak_repository.dart b/lib/src/repositories/kontak_repository.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b0b996da2c25486c64ceb9d30f5a1ba89fb83821
--- /dev/null
+++ b/lib/src/repositories/kontak_repository.dart
@@ -0,0 +1,22 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/models/kontak.dart';
+
+class KontakRepository {
+  CollectionReference get kontakSigapCollections => Firestore.instance.collection('waspada/covid19/kontak');
+
+  Stream<QuerySnapshot> get kontekSigapSnapshots => kontakSigapCollections.snapshots();
+
+  CollectionReference kontakCollections(String institusiId) =>
+      Firestore.instance.collection('waspada/covid19/institusi/${institusiId}/kontak');
+
+  Stream<QuerySnapshot> kontakSnapshots(String institusiId) =>
+      kontakCollections(institusiId).snapshots();
+
+  List<Kontak> semuaKontak(AsyncSnapshot<QuerySnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    return snapshot.data.documents
+        .map<Kontak>((document) => Kontak.fromJson(document.data))
+        .toList();
+  }
+}
diff --git a/lib/src/repositories/notifikasi_repository.dart b/lib/src/repositories/notifikasi_repository.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c037097a367e874b6fc55e741531360e60c04028
--- /dev/null
+++ b/lib/src/repositories/notifikasi_repository.dart
@@ -0,0 +1,14 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/models/notifikasi.dart';
+
+class NotifikasiRepository {
+  CollectionReference get notifikasiCollections => Firestore.instance.collection('waspada/covid19/notifikasi');
+
+  Stream<QuerySnapshot> get notifikasiSnapshots => notifikasiCollections.snapshots();
+
+  List<Notifikasi> semuaNotifikasi(AsyncSnapshot<QuerySnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    return snapshot.data.documents.map<Notifikasi>((document) => Notifikasi.fromJson(document.data)).toList();
+  }
+}
diff --git a/lib/src/repositories/product_supplier_repository.dart b/lib/src/repositories/product_supplier_repository.dart
new file mode 100644
index 0000000000000000000000000000000000000000..82b827d7e96fc62a485bb3ab7b2d13e31f641e7a
--- /dev/null
+++ b/lib/src/repositories/product_supplier_repository.dart
@@ -0,0 +1,36 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/models/product_supplier.dart';
+
+class ProductSupplierRepository {
+  DocumentReference oneProductFromSupplier(
+      {@required String idSupplier, @required String idProduct}) {
+    return Firestore.instance
+        .document('waspada/covid19/supplier/$idSupplier/barang/$idProduct');
+  }
+
+  DocumentReference oneProductFromSupplierPath({@required String path}) {
+    return Firestore.instance.document(path);
+  }
+
+  ProductSupplier product(AsyncSnapshot<DocumentSnapshot> snapshot) {
+    Map<String, dynamic> product;
+    if (!snapshot.hasData || snapshot.hasError) return null;
+    product = snapshot.data.data;
+    final itemDocId = snapshot.data['item'].documentID;
+    return ProductSupplier.fromJson(
+        product..addAll({'idItem': itemDocId, 'id': snapshot.data.documentID}));
+  }
+
+  CollectionReference allProductsFromSupplier({@required String idSupplier}) =>
+      Firestore.instance.collection('waspada/covid19/supplier/$idSupplier/barang');
+
+  List<ProductSupplier> allProducts(AsyncSnapshot<QuerySnapshot> snapshot, {String search}) {
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    //var docs = snapshot.data.documents;
+    return snapshot.data.documents
+        .map<ProductSupplier>((document) => ProductSupplier.fromJson(
+            document.data..addAll({'id': document.documentID, 'idItem': document.data['item'].documentID})))
+        .toList();
+  }
+}
diff --git a/lib/src/repositories/supplier_repository.dart b/lib/src/repositories/supplier_repository.dart
new file mode 100644
index 0000000000000000000000000000000000000000..15cef593bfa87ecabc7445ea96dd4b20f647f81e
--- /dev/null
+++ b/lib/src/repositories/supplier_repository.dart
@@ -0,0 +1,34 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/models/supplier.dart';
+
+class SupplierRepository {
+  CollectionReference get supplierCollections =>
+      Firestore.instance.collection('waspada/covid19/supplier');
+
+
+  DocumentReference supplierDocument({@required String idSupplier}) {
+    return Firestore.instance.document('waspada/covid19/supplier/$idSupplier');
+  }
+
+  DocumentReference supplierDocumentFromPath({@required String path}) {
+    return Firestore.instance.document(path);
+  }
+
+  Supplier supplier(AsyncSnapshot<DocumentSnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return null;
+    return Supplier.fromJson(
+        snapshot.data.data..addAll({'id': snapshot.data.documentID}));
+  }
+
+  Stream<QuerySnapshot> get supplierSnapshots =>
+      supplierCollections.snapshots();
+
+  List<Supplier> semuaSupplier(AsyncSnapshot<QuerySnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    return snapshot.data.documents
+        .map<Supplier>((document) => Supplier.fromJson(
+            document.data..addAll({'id': document.documentID})))
+        .toList();
+  }
+}
diff --git a/lib/src/repositories/ulasan_repositories.dart b/lib/src/repositories/ulasan_repositories.dart
new file mode 100644
index 0000000000000000000000000000000000000000..259683dfe57ae2fefa733b4b532825608b03e907
--- /dev/null
+++ b/lib/src/repositories/ulasan_repositories.dart
@@ -0,0 +1,25 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/models/ulasan.dart';
+
+class UlasanRepository {
+  List<Ulasan> allUlasan(AsyncSnapshot<QuerySnapshot> snapshot) {
+    if (!snapshot.hasData || snapshot.hasError) return [];
+    return snapshot.data.documents
+        .map<Ulasan>((document) => Ulasan.fromJson(document.data))
+        .toList();
+  }
+
+  CollectionReference ulasanSupplierCollections(String idSupplier, String collection) =>
+      Firestore.instance.collection('waspada/covid19/supplier/$idSupplier/$collection');
+
+  Stream<QuerySnapshot> ulasanSupplierSnapshots(String idSupplier, {String collection = 'ulasan'}) =>
+      ulasanSupplierCollections(idSupplier, collection).snapshots();
+
+  // TODO after home-industry Firestore is ready
+  // CollectionReference ulasanIndustryCollections(String idIndustry) =>
+  //     Firestore.instance.collection('waspada/covid19/industries/$idIndustry/ulasan');
+
+  // Stream<QuerySnapshot> ulasanIndustrySnapshots(String idIndustry) =>
+  //     ulasanIndustryCollections(idIndustry).snapshots();
+}
diff --git a/lib/src/routes.dart b/lib/src/routes.dart
index a23b36e2a14da0a6e3d7f3618152ad0a0ec08796..1faea815377ac8978dbcc8adba8fd25b2a753ad4 100644
--- a/lib/src/routes.dart
+++ b/lib/src/routes.dart
@@ -1,37 +1,127 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/widgets.dart';
-
-import 'screens/home/home_screen.dart';
+import 'package:mobile_apps/src/components/bottom_navigation_bar.dart';
+import 'package:mobile_apps/src/screens/detail_item_needs/detail_item.dart';
+import 'package:mobile_apps/src/screens/home/home_industry/all_home_industry.dart';
+import 'package:mobile_apps/src/screens/home/institusi/all_institusi.dart';
+import 'package:mobile_apps/src/screens/home/inventories/all_inventories.dart';
+import 'package:mobile_apps/src/screens/homeindustry/home_industry.dart';
+import 'package:mobile_apps/src/screens/institusi-screen/institusi_screen.dart';
+import 'package:fluro/fluro.dart';
+import 'package:mobile_apps/src/screens/supplier/all_suppliers.dart';
+import 'package:mobile_apps/src/screens/supplier/detailsupplier.dart';
+import 'package:mobile_apps/src/screens/supplier/product/detailproduct.dart';
 import 'screens/login/login_screen.dart';
+import 'screens/notification/notification_screen.dart';
 
 class AppRoute {
-  final String route;
-  final WidgetBuilder widgetBuilder;
+  // Add basic routing here
+  static String home = '/home';
+  static String login = '/login';
+  static String notification = '/notification';
+  static String recap = '/recap';
+  static String allInstitusi = '/allInstitusi';
+  static String allSuppliers = '/allSuppliers';
+  static String allHomeIndustry = '/allHomeIndustry';
 
-  AppRoute(this.route, this.widgetBuilder) : assert(route.startsWith('/')), assert(widgetBuilder != null);
-}
+  // Add wildcard routing here
+  static String institusi(
+          {@required String idInstitusi, @required String namaInstitusi}) =>
+      '/institusi/idInstitusi/$idInstitusi/namaInstitusi/$namaInstitusi';
+  static String itemNeeded(
+          {@required String idInstitusi,
+          @required String idItemNeeded,
+          @required String namaInstitusi}) =>
+      '/itemNeeded/idItemNeeded/$idItemNeeded/idInstitusi/$idInstitusi/namaInstitusi/$namaInstitusi';
+  static String detailProductSupplier(
+      {@required String idSupplier, @required String idBarang}) {
+    return 'supplier/idSupplier/$idSupplier/idBarang/$idBarang';
+  }
 
-class AppRoutes {
-  // Add known registered routes in this variable
-  static final List<AppRoute> _registeredRoutes = [
-    AppRoutes.home,
-    AppRoutes.login,
-  ];
+  static String supplier({
+    @required String idSupplier,
+  }) =>
+      '/supplier/idSupplier/$idSupplier';
+  static String homeIndustry({@required String id}) => '/homeindustry/$id';
 
-  static Map<String, WidgetBuilder> get routes => Map.fromEntries(
-        _registeredRoutes.map(
-          (route) =>
-              MapEntry<String, WidgetBuilder>(route.route, route.widgetBuilder),
-        ),
-      );
+  static Router router = Router();
+  // Make handler for every page
+  static final Handler _homeHandler = Handler(
+      handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
+          BottomNavigation());
+  static final Handler _loginHandler = Handler(
+      handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
+          LoginScreen());
+  static final Handler _notificationHandler = Handler(
+      handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
+          NotificationScreen());
+  static final Handler _recapHandler = Handler(
+      handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
+          AllInventoriesWidget());
+  static final Handler _allInstitusiHandler = Handler(
+      handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
+          AllInstitusiWidget());
+  static final Handler _allSuppliersHandler = Handler(
+      handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
+          AllSuppliersWidget());
 
-  // Route for Home Screen
-  static final AppRoute home = AppRoute('/home', (_) => HomeScreen());
+  static final Handler _institusiHandler = Handler(
+      handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
+          InstitusiScreen(
+              namaInstitusi: changeEscapedString(params['namaInstitusi'][0]),
+              idInstitusi: params['idInstitusi'][0]));
+  static final Handler _itemNeededHandler = Handler(
+      handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
+          DetailItemNeeds(
+              idInstitusi: params['idInstitusi'][0],
+              idItemNeeded: params['idItemNeeded'][0],
+              institusiName: changeEscapedString(params['namaInstitusi'][0])));
+  static final Handler _detailProductSupplierHandler = Handler(
+      handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
+          DetailProductSupplier(
+            idBarang: params['idBarang'][0],
+            idSupplier: params['idSupplier'][0],
+          ));
 
-  // Route for Login Screen
-  static final AppRoute login = AppRoute('/login', (_) => LoginScreen());
+  static final Handler _supplierHandler = Handler(
+      handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
+          DetailSupplier(
+            idSupplier: params['idSupplier'][0],
+          ));
+  static final Handler _homeIndustryHandler = Handler(
+      handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
+          HomeIndustryPage(id: params['id'][0]));
+  static final Handler _allHomeIndustryHandler = Handler(
+      handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
+          AllHomeIndustry());
+  static void setupRouter() {
+    // define all route here
+    router.define(
+      '/home',
+      handler: _homeHandler,
+    );
+    router.define(
+      '/login',
+      handler: _loginHandler,
+    );
+    router.define('/notification', handler: _notificationHandler);
+    router.define('/recap', handler: _recapHandler);
+    router.define('/allInstitusi', handler: _allInstitusiHandler);
+    router.define('/allSuppliers', handler: _allSuppliersHandler);
+    router.define(
+        '/institusi/idInstitusi/:idInstitusi/namaInstitusi/:namaInstitusi',
+        handler: _institusiHandler);
+    router.define(
+        '/itemNeeded/idItemNeeded/:idItemNeeded/idInstitusi/:idInstitusi/namaInstitusi/:namaInstitusi',
+        handler: _itemNeededHandler);
+    router.define('supplier/idSupplier/:idSupplier/idBarang/:idBarang',
+        handler: _detailProductSupplierHandler);
+    router.define(
+        '/supplier/idSupplier/:idSupplier',
+        handler: _supplierHandler);
+    router.define('/homeindustry/:id', handler: _homeIndustryHandler);
+    router.define('/allHomeIndustry', handler: _allHomeIndustryHandler);
+  }
 
-  /** 
-   * Add new Route definition below here and add it to [AppRoutes._registeredRoutes]
-   */
+  static String changeEscapedString(text) => Uri.decodeComponent(text);
 }
diff --git a/lib/src/screens/detail_item_needs/detail_item.dart b/lib/src/screens/detail_item_needs/detail_item.dart
new file mode 100644
index 0000000000000000000000000000000000000000..24da6cf3bf212810e4d15738cd5c01996163ac64
--- /dev/null
+++ b/lib/src/screens/detail_item_needs/detail_item.dart
@@ -0,0 +1,171 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/painting.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:intl/intl.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/common/functions.dart';
+import 'package:mobile_apps/src/components/cached_image/cached_image_network.dart';
+import 'package:mobile_apps/src/models/item.dart';
+import 'package:mobile_apps/src/models/item_needed.dart';
+import 'package:mobile_apps/src/repositories/item_needed_repositories.dart';
+
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+import 'package:mobile_apps/src/repositories/item_repository.dart';
+
+class DetailItemNeeds extends StatelessWidget {
+  final String idInstitusi;
+  final String idItemNeeded;
+  final String institusiName;
+
+  const DetailItemNeeds(
+      {Key key,
+      @required this.idInstitusi,
+      @required this.idItemNeeded,
+      @required this.institusiName})
+      : super(key: key);
+  @override
+  Widget build(BuildContext context) {
+    final itemNeededRepo = ItemNeededRepository(
+        idInstitusi: idInstitusi, idItemNeeded: idItemNeeded);
+    return Scaffold(
+        backgroundColor: Colors.white,
+        appBar: AppBar(
+          title: Text(institusiName),
+          centerTitle: true,
+          actions: <Widget>[
+            IconButton(
+              icon: Icon(Icons.share),
+              onPressed: () => shareScreenshot(context, institusiName),
+            ),
+            IconButton(icon: Icon(Icons.notifications), onPressed: () {})
+          ],
+        ),
+        body: StreamBuilder(
+          stream: itemNeededRepo.itemNeededDocument.snapshots(),
+          builder: (_, snapshot) {
+            if (snapshot.hasError) {
+              SchedulerBinding.instance.addPostFrameCallback((_) {
+                Scaffold.of(context).showErrorSnackBar(
+                    'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+              });
+            }
+            ;
+            if (snapshot.connectionState == ConnectionState.waiting) {
+              return Center(child: CircularProgressIndicator());
+            }
+            ;
+            final itemNeeded = itemNeededRepo.itemNeeded(snapshot);
+            final itemRepo = ItemRepository();
+            return StreamBuilder(
+              stream: itemRepo
+                  .itemDocumentFromPath(path: itemNeeded.itemPath)
+                  .snapshots(),
+              builder: (_, snapshotItem) {
+                if (snapshotItem.hasError) {
+                  SchedulerBinding.instance.addPostFrameCallback((_) {
+                    Scaffold.of(context).showErrorSnackBar(
+                        'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                  });
+                }
+                ;
+                if (snapshotItem.connectionState == ConnectionState.waiting) {
+                  return Center(child: CircularProgressIndicator());
+                }
+                ;
+                final item = itemRepo.item(snapshotItem);
+                return _BodyItemNeed(itemNeeded: itemNeeded, item: item);
+              },
+            );
+          },
+        ));
+  }
+}
+
+class _BodyItemNeed extends StatelessWidget {
+  final ItemNeeded itemNeeded;
+  final Item item;
+  const _BodyItemNeed({
+    Key key,
+    @required this.itemNeeded,
+    @required this.item,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: EdgeInsets.symmetric(
+          horizontal: MarginConstants.horizontalFromScreen,
+          vertical: MarginConstants.verticalFromScreen),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: <Widget>[
+          Expanded(
+            flex: 1,
+            child: CustomCachedNetworkImage(imageUrl: item.url),
+          ),
+          Expanded(
+            flex: 3,
+            child: Padding(
+              padding: const EdgeInsets.only(top: 40),
+              child: Column(
+                mainAxisAlignment: MainAxisAlignment.spaceAround,
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: <Widget>[
+                  _LabelValueDescription(
+                    label: 'Nama Barang',
+                    value: item.nama,
+                  ),
+                  _LabelValueDescription(
+                      label: 'Kuantitas Kebutuhan',
+                      value: '${itemNeeded.kebutuhan}'),
+                  _LabelValueDescription(
+                      label: 'Kuantitas terpenuhi',
+                      value: '${itemNeeded.terpenuhi}'),
+                  _LabelValueDescription(
+                      label: 'Periode Kebutuhan',
+                      value:
+                          '${DateFormat('dd MMMM').format(itemNeeded.periodeMulai)} - '
+                          '${DateFormat('dd MMMM yyyy').format(itemNeeded.periodeTerakhir)}'),
+                  _LabelValueDescription(
+                    label: 'Spesifikasi',
+                    value: item.deskripsi,
+                  ),
+                  _LabelValueDescription(label: 'Manfaat', value: item.manfaat)
+                ],
+              ),
+            ),
+          )
+        ],
+      ),
+    );
+  }
+}
+
+class _LabelValueDescription extends StatelessWidget {
+  final String label;
+  final String value;
+
+  const _LabelValueDescription(
+      {Key key, @required this.label, @required this.value})
+      : super(key: key);
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: <Widget>[
+        Text(
+          label,
+          style: TextStyle(fontWeight: FontWeight.w800),
+          maxLines: 1,
+          overflow: TextOverflow.ellipsis,
+        ),
+        Text(
+          value,
+          style: TextStyle(fontSize: 18),
+          maxLines: 3,
+          overflow: TextOverflow.ellipsis,
+        )
+      ],
+    );
+  }
+}
diff --git a/lib/src/screens/home/home_industry/all_home_industry.dart b/lib/src/screens/home/home_industry/all_home_industry.dart
new file mode 100644
index 0000000000000000000000000000000000000000..485473ced861251f613e591d9527005a1563a12a
--- /dev/null
+++ b/lib/src/screens/home/home_industry/all_home_industry.dart
@@ -0,0 +1,103 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/appbar/sigapappbar.dart';
+import 'package:mobile_apps/src/repositories/home_industry_repository.dart';
+// Extension import
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+import 'package:mobile_apps/src/screens/home/home_industry/home_industry.dart';
+
+class AllHomeIndustry extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      backgroundColor: ColorConstants.backgroundColor,
+      appBar: SigapAppBar(title: 'Home Industry'),
+      body: SingleChildScrollView(
+        padding: EdgeInsets.symmetric(
+            horizontal: MarginConstants.horizontalFromScreen,
+            vertical: MarginConstants.verticalFromScreen),
+        child: _AllHomeIndustryBody(),
+      ),
+    );
+  }
+}
+
+class _AllHomeIndustryBody extends StatefulWidget {
+  @override
+  __AllHomeIndustryBodyState createState() => __AllHomeIndustryBodyState();
+}
+
+class __AllHomeIndustryBodyState extends State<_AllHomeIndustryBody> {
+  List homeIndustries = [];
+  List currentSearch;
+
+  void updateSearch(String text) {
+    var tempList = homeIndustries.where((homeIndustry) {
+      var search =
+          homeIndustry.nama.toLowerCase() + homeIndustry.alamat.toLowerCase();
+      return search.contains(text.toLowerCase());
+    });
+    setState(() {
+      currentSearch = List.from(tempList);
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final repository = HomeIndustryRepository();
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: <Widget>[
+        Padding(
+            padding: EdgeInsets.only(bottom: 12.0),
+            child: Text(
+              'Home Industry',
+              style: Theme.of(context).textTheme.headline5,
+            )),
+        Padding(
+            padding: EdgeInsets.only(bottom: 12.0),
+            child: Text(
+              'Home Industry dapat memproduksi produk medis sesuai permintaan (pre-order). Lihat daftar home industry dan produknya di sini.',
+              style: Theme.of(context).textTheme.caption,
+            )),
+        Padding(
+            padding: EdgeInsets.only(bottom: 12.0),
+            child: TextField(
+              onChanged: updateSearch,
+              decoration: InputDecoration(
+                  labelText: 'Cari nama home industry',
+                  hintText: 'Cari nama home industry',
+                  prefixIcon: Icon(Icons.search),
+                  border: OutlineInputBorder(
+                      borderRadius: BorderRadius.all(Radius.circular(25.0)))),
+            )),
+        StreamBuilder(
+          stream: repository.homeIndustryCollections.snapshots(),
+          builder: (_, snapshot) {
+            if (snapshot.hasError) {
+              SchedulerBinding.instance.addPostFrameCallback((_) {
+                Scaffold.of(context).showErrorSnackBar(
+                    'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+              });
+            }
+            if (snapshot.connectionState == ConnectionState.waiting) {
+              return Center(child: CircularProgressIndicator());
+            }
+            homeIndustries = repository.semuaHomeIndustry(snapshot).toList();
+            var shown = currentSearch ?? homeIndustries;
+            return Column(
+              children: [
+                ...shown
+                    .map((homeIndustry) => HomeIndustryWidget(
+                          homeIndustry: homeIndustry,
+                        ))
+                    .toList(),
+              ],
+            );
+          },
+        ),
+      ],
+    );
+  }
+}
diff --git a/lib/src/screens/home/home_industry/home_industry.dart b/lib/src/screens/home/home_industry/home_industry.dart
new file mode 100644
index 0000000000000000000000000000000000000000..da4e55db7fe4f235bc18dc4a74d1676e1ae3297c
--- /dev/null
+++ b/lib/src/screens/home/home_industry/home_industry.dart
@@ -0,0 +1,71 @@
+import 'package:flutter/material.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/buttons/buttons.dart';
+import 'package:mobile_apps/src/models/home_industry.dart';
+import 'package:mobile_apps/src/routes.dart';
+
+class HomeIndustryWidget extends StatelessWidget {
+  final HomeIndustry homeIndustry;
+
+  HomeIndustryWidget({@required this.homeIndustry, Key key}) : super(key: key);
+  @override
+  Widget build(BuildContext context) {
+    return Card(
+        child: FittedBox(
+      fit: BoxFit.fitHeight,
+      child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          mainAxisAlignment: MainAxisAlignment.center,
+          mainAxisSize: MainAxisSize.min,
+          children: <Widget>[
+            Container(
+              width: 380,
+              margin: EdgeInsets.only(top: 20),
+              child: ListTile(
+                title: Text(homeIndustry.nama,
+                    style: Theme.of(context).textTheme.headline6),
+                subtitle: Text('${homeIndustry.alamat}',
+                    style: Theme.of(context).textTheme.subtitle1),
+                trailing: Icon(Icons.share),
+              ),
+            ),
+            Padding(
+              padding: const EdgeInsets.only(left: 12),
+              child: Row(
+                mainAxisAlignment: MainAxisAlignment.start,
+                children: <Widget>[
+                  Icon(
+                    Icons.home,
+                    color: ColorConstants.grayPrimary,
+                  ),
+                  Text(
+                    'Home Industry',
+                    style: TextStyle(fontSize: 12),
+                  )
+                ],
+              ),
+            ),
+            Row(
+              mainAxisAlignment: MainAxisAlignment.end,
+              children: <Widget>[
+                Padding(
+                  padding:
+                      EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
+                  child: RoundedButton(text: 'Detail',
+                      elevation: 0.0,
+                      padding: EdgeInsets.symmetric(horizontal: 66.0),
+                      radius: 32.0,
+                      style: TextStyle(
+                          color: Colors.white,
+                          fontSize: 12.0,
+                          fontWeight: FontWeight.bold), onPressed: () {
+                    Navigator.of(context)
+                        .pushNamed(AppRoute.homeIndustry(id: homeIndustry.id));
+                  }),
+                ),
+              ],
+            ),
+          ]),
+    ));
+  }
+}
diff --git a/lib/src/screens/home/home_industry/list_home_industry.dart b/lib/src/screens/home/home_industry/list_home_industry.dart
new file mode 100644
index 0000000000000000000000000000000000000000..5de0d9594a5e950edb0c3c86e4e37f1542dc57ee
--- /dev/null
+++ b/lib/src/screens/home/home_industry/list_home_industry.dart
@@ -0,0 +1,76 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/repositories/home_industry_repository.dart';
+// Extension import
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+import 'package:mobile_apps/src/routes.dart';
+import 'package:mobile_apps/src/screens/home/home_industry/home_industry.dart';
+
+class ListHomeIndustry extends StatefulWidget {
+  @override
+  _ListHomeIndustryState createState() => _ListHomeIndustryState();
+}
+
+class _ListHomeIndustryState extends State<ListHomeIndustry> {
+  @override
+  Widget build(BuildContext context) {
+    final repository = HomeIndustryRepository();
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: <Widget>[
+        Row(
+          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+          children: <Widget>[
+            Padding(
+                padding: EdgeInsets.only(bottom: 12.0),
+                child: Text(
+                  'Home Industry',
+                  style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
+                )),
+            Padding(
+                padding: EdgeInsets.only(bottom: 12.0),
+                child: GestureDetector(
+                  onTap: () {
+                    Navigator.of(context).pushNamed(AppRoute.allHomeIndustry);
+                  },
+                  child: Text(
+                    'Lihat Semua',
+                    style: TextStyle(color: ColorConstants.redPrimary),
+                  ),
+                )),
+          ],
+        ),
+        StreamBuilder(
+          stream: repository.homeIndustryCollections.snapshots(),
+          builder: (_, snapshot) {
+            if (snapshot.hasError) {
+              SchedulerBinding.instance.addPostFrameCallback((_) {
+                Scaffold.of(context).showErrorSnackBar(
+                    'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+              });
+            }
+            if (snapshot.connectionState == ConnectionState.waiting) {
+              return Center(child: CircularProgressIndicator());
+            }
+            final semuaHomeIndustry =
+                repository.semuaHomeIndustry(snapshot).take(3).toList();
+            return Container(
+              height: MediaQuery.of(context).size.height * 0.265,
+              child: ListView.builder(
+                itemBuilder: (BuildContext context, int index) {
+                  return HomeIndustryWidget(
+                    homeIndustry: semuaHomeIndustry[index],
+                  );
+                },
+                itemCount: semuaHomeIndustry.length,
+                shrinkWrap: true,
+                scrollDirection: Axis.horizontal,
+              ),
+            );
+          },
+        ),
+      ],
+    );
+  }
+}
diff --git a/lib/src/screens/home/home_screen.dart b/lib/src/screens/home/home_screen.dart
index 3bd8bfcd561d74914ad5799571c4a14b33ce9838..d04992759b06b5a255c9159d8a8d8977c3532b4c 100644
--- a/lib/src/screens/home/home_screen.dart
+++ b/lib/src/screens/home/home_screen.dart
@@ -1,64 +1,60 @@
 import 'package:flutter/material.dart';
-import 'package:flutter/scheduler.dart';
+import 'package:flutter/rendering.dart';
 import 'package:flutter/widgets.dart';
 import 'package:mobile_apps/src/common/constants.dart';
-import 'package:mobile_apps/src/screens/home/inventories/inventories.dart';
+import 'package:mobile_apps/src/screens/home/home_industry/list_home_industry.dart';
+import 'package:mobile_apps/src/screens/home/institusi/list_institusi.dart';
+/* TODO remove comment after Agregasi feature ready
+import 'package:mobile_apps/src/screens/home/inventories/inventories.dart'; */
+import 'package:mobile_apps/src/screens/home/kontak/kontak.dart';
+import 'package:mobile_apps/src/screens/supplier/list_supplier.dart';
 import 'package:mobile_apps/src/services/auth_service.dart';
 import 'package:provider/provider.dart';
 
-import '../../routes.dart';
-
 class HomeScreen extends StatelessWidget {
+  const HomeScreen({Key key}) : super(key: key);
   @override
   Widget build(BuildContext context) {
     final authService = Provider.of<AuthService>(context);
-    if (!authService.isAuthenticated) {
-      SchedulerBinding.instance.addPostFrameCallback((_) {
-        Navigator.of(context).pushReplacementNamed(AppRoutes.login.route);
-      });
-      return Container();
-    }
     final account = authService.account;
-    return Scaffold(
-      body: ListView(
+    return SingleChildScrollView(
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
         children: <Widget>[
+          if (authService.isAuthenticated)
+            Text(
+              'Halo,\n${account?.displayName}!',
+              style: Theme.of(context).textTheme.subtitle1,
+              textAlign: TextAlign.left,
+            ),
+          /* TODO remove comment after Agregasi feature ready
+          Container(
+            padding: EdgeInsets.only(top: 31, left: 20, right: 20, bottom: 13),
+            color: Colors.white,
+            child: InventoriesWidget(),
+          ),*/
+          Container(
+              padding: EdgeInsets.symmetric(
+                  horizontal: MarginConstants.horizontalFromScreen,
+                  vertical: 15.0),
+              child: ListInstitusiWidget()),
+          Container(
+              padding: EdgeInsets.symmetric(
+                  horizontal: MarginConstants.horizontalFromScreen,
+                  vertical: 15.0),
+              child: ListSupplierWidget()),
+            Container(
+              padding: EdgeInsets.symmetric(
+                  horizontal: MarginConstants.horizontalFromScreen,
+                  vertical: 15.0),
+              child: ListHomeIndustry()),  
           Container(
             margin: EdgeInsets.symmetric(
-              horizontal: MarginConstants.horizontalFromScreen,
               vertical: MarginConstants.verticalFromScreen,
             ),
-            child: Column(
-              crossAxisAlignment: CrossAxisAlignment.start,
-              children: <Widget>[
-                Text(
-                  'Halo,\n${account.displayName}!',
-                  style: Theme.of(context).textTheme.subtitle1,
-                  textAlign: TextAlign.left,
-                ),
-                Padding(
-                  padding: EdgeInsets.only(top: 24.0),
-                  child: InventoriesWidget(),
-                ),
-              ],
-            ),
-          ),
-        ],
-      ),
-      appBar: AppBar(
-        title:
-            Text('Siaga Wabah', style: TextStyle(fontWeight: FontWeight.bold)),
-        actions: <Widget>[
-          Container(
-            child: Icon(Icons.share),
-            margin: EdgeInsets.only(right: 8.0),
-          ),
-          Container(
-            margin:
-                EdgeInsets.only(right: MarginConstants.horizontalFromScreen * 2),
-            child: Icon(Icons.notifications),
+            child: KontakWidget(),
           ),
         ],
-        centerTitle: true,
       ),
     );
   }
diff --git a/lib/src/screens/home/institusi/all_institusi.dart b/lib/src/screens/home/institusi/all_institusi.dart
new file mode 100644
index 0000000000000000000000000000000000000000..619637e77d11dfddd130bd470277d23e81a29d0d
--- /dev/null
+++ b/lib/src/screens/home/institusi/all_institusi.dart
@@ -0,0 +1,108 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/appbar/sigapappbar.dart';
+import 'package:mobile_apps/src/repositories/institusi_repository.dart';
+
+// Extension import
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+
+import 'institusi.dart';
+
+class AllInstitusiWidget extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      backgroundColor: ColorConstants.backgroundColor,
+      appBar: SigapAppBar(title: 'Semua Institusi'),
+      body: ListView(
+        padding: EdgeInsets.symmetric(
+            horizontal: MarginConstants.horizontalFromScreen,
+            vertical: MarginConstants.verticalFromScreen),
+        children: <Widget>[
+          _AllInstitusiBody(),
+        ],
+      ),
+    );
+  }
+}
+
+class _AllInstitusiBody extends StatefulWidget {
+  @override
+  _AllInstitusiBodyState createState() => _AllInstitusiBodyState();
+}
+
+class _AllInstitusiBodyState extends State<_AllInstitusiBody> {
+  List semuaInstitusi = [];
+  List currentSearch;
+
+  void updateSearch(String text) {
+    var tempList = semuaInstitusi.where((institusi) {
+      var search =
+          institusi.nama.toLowerCase() + institusi.alamat.toLowerCase();
+      return search.contains(text.toLowerCase());
+    });
+    setState(() {
+      currentSearch = List.from(tempList);
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final repository = InstitusiRepository();
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: <Widget>[
+        Padding(
+            padding: EdgeInsets.only(bottom: 12.0),
+            child: Text(
+              'Rumah Sakit & Klinik',
+              style: Theme.of(context).textTheme.headline5,
+            )),
+        Padding(
+            padding: EdgeInsets.only(bottom: 12.0),
+            child: Text(
+              'Rumah Sakit dan Klinik kesulitan memenuhi kebutuhan-kebutuhannya. Lihat daftar rumah sakit dan kebutuhannya di sini.',
+              style: Theme.of(context).textTheme.caption,
+            )),
+        Padding(
+            padding: EdgeInsets.only(bottom: 12.0),
+            child: TextField(
+              onChanged: updateSearch,
+              decoration: InputDecoration(
+                  labelText: 'Cari nama rumah sakit',
+                  hintText: 'Cari nama rumah sakit',
+                  prefixIcon: Icon(Icons.search),
+                  border: OutlineInputBorder(
+                      borderRadius: BorderRadius.all(Radius.circular(25.0)))),
+            )),
+        StreamBuilder(
+          stream: repository.institusiCollections.snapshots(),
+          builder: (_, snapshot) {
+            if (snapshot.hasError) {
+              SchedulerBinding.instance.addPostFrameCallback((_) {
+                Scaffold.of(context).showErrorSnackBar(
+                    'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+              });
+            }
+            if (snapshot.connectionState == ConnectionState.waiting) {
+              return Center(child: CircularProgressIndicator());
+            }
+            semuaInstitusi = repository.semuaInstitusi(snapshot).toList();
+            var shown = currentSearch ?? semuaInstitusi;
+            return Column(
+              children: [
+                ...shown
+                    .map((institusi) => InstitusiWidget(
+                          institusi: institusi,
+                        ))
+                    .toList(),
+              ],
+            );
+          },
+        ),
+      ],
+    );
+  }
+}
diff --git a/lib/src/screens/home/institusi/institusi.dart b/lib/src/screens/home/institusi/institusi.dart
new file mode 100644
index 0000000000000000000000000000000000000000..4d43b0219605331edad3dafb8a10721ffd277495
--- /dev/null
+++ b/lib/src/screens/home/institusi/institusi.dart
@@ -0,0 +1,78 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/components/buttons/buttons.dart';
+import 'package:mobile_apps/src/models/institusi.dart';
+import 'package:mobile_apps/src/routes.dart';
+
+class InstitusiWidget extends StatelessWidget {
+  final Institusi institusi;
+
+  const InstitusiWidget({Key key, @required this.institusi})
+      : assert(institusi != null),
+        super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Card(
+        child: FittedBox(
+          fit: BoxFit.fitHeight,
+          child: Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              mainAxisAlignment: MainAxisAlignment.center,
+              mainAxisSize: MainAxisSize.min,
+              children: <Widget>[
+            Container(
+              width: 380,
+              margin: EdgeInsets.only(top: 20),
+              child: ListTile(
+                title: Text(institusi.nama,
+                    style: Theme.of(context).textTheme.headline6),
+                subtitle: Text('${institusi.alamat}',
+                    style: Theme.of(context).textTheme.subtitle1),
+                trailing: Icon(Icons.share),
+              ),
+            ),
+            Padding(
+                padding: EdgeInsets.symmetric(horizontal: 16.0),
+                child: Text('${institusi.info1} | ${institusi.info2}',
+                    style: Theme.of(context).textTheme.subtitle2)),
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: <Widget>[
+                Padding(
+                  padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
+                  child: RoundedButton(text: 'Subscribe',
+                      elevation: 0.0,
+                      padding: EdgeInsets.symmetric(horizontal: 50.0),
+                      radius: 32.0,
+                      style: TextStyle(
+                          color: Colors.white,
+                          fontSize: 12.0,
+                          fontWeight: FontWeight.bold),
+                      onPressed: () {}),
+                ),
+                Padding(
+                  padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
+                  child: RoundedButton(text: 'Detail',
+                      elevation: 0.0,
+                      padding: EdgeInsets.symmetric(horizontal: 66.0),
+                      radius: 32.0,
+                      style: TextStyle(
+                          color: Colors.white,
+                          fontSize: 12.0,
+                          fontWeight: FontWeight.bold), onPressed: () {
+                    Navigator.of(context).pushNamed(AppRoute.institusi(
+                        namaInstitusi: institusi.nama,
+                        idInstitusi: institusi.id));
+                  }),
+                ),
+              ],
+            ),
+            // Padding(
+            //     padding: EdgeInsets.symmetric(horizontal: 16.0)
+            //         .copyWith(top: 16.0),
+            //     child: ProgressBar(20, 100))
+          ]),
+        ));
+  }
+}
diff --git a/lib/src/screens/home/institusi/list_institusi.dart b/lib/src/screens/home/institusi/list_institusi.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e72cb8f0ea9739372863771d8806eb9ecf525fa1
--- /dev/null
+++ b/lib/src/screens/home/institusi/list_institusi.dart
@@ -0,0 +1,79 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/repositories/institusi_repository.dart';
+
+// Extension import
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+
+import '../../../routes.dart';
+import 'institusi.dart';
+
+class ListInstitusiWidget extends StatefulWidget {
+  @override
+  _ListInstitusiWidgetState createState() => _ListInstitusiWidgetState();
+}
+
+class _ListInstitusiWidgetState extends State<ListInstitusiWidget> {
+  // List semuaInstitusi = [];
+  @override
+  Widget build(BuildContext context) {
+    final repository = InstitusiRepository();
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: <Widget>[
+        Row(
+          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+          children: <Widget>[
+            Padding(
+                padding: EdgeInsets.only(bottom: 12.0),
+                child: Text(
+                  'Rumah Sakit & Klinik',
+                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
+                )),
+            Padding(
+                padding: EdgeInsets.only(bottom: 12.0),
+                child: GestureDetector(
+                  onTap: () {
+                    Navigator.of(context).pushNamed(AppRoute.allInstitusi);
+                  },
+                  child: Text(
+                    'Lihat Semua',
+                    style: TextStyle(color: ColorConstants.redPrimary),
+                  ),
+                )),
+          ],
+        ),
+        StreamBuilder(
+          stream: repository.institusiCollections.snapshots(),
+          builder: (_, snapshot) {
+            if (snapshot.hasError) {
+              SchedulerBinding.instance.addPostFrameCallback((_) {
+                Scaffold.of(context).showErrorSnackBar(
+                    'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+              });
+            }
+            if (snapshot.connectionState == ConnectionState.waiting) {
+              return Center(child: CircularProgressIndicator());
+            }
+            final semuaInstitusi = repository.semuaInstitusi(snapshot).take(3).toList();
+            return Container(
+              height: MediaQuery.of(context).size.height * 0.265,
+              child: ListView.builder(
+                itemBuilder: (BuildContext context, int index) {
+                  return InstitusiWidget(
+                    institusi: semuaInstitusi[index],
+                  );
+                },
+                itemCount: semuaInstitusi.length,
+                shrinkWrap: true,
+                scrollDirection: Axis.horizontal,
+              ),
+            );
+          },
+        ),
+      ],
+    );
+  }
+}
diff --git a/lib/src/screens/home/inventories/all_inventories.dart b/lib/src/screens/home/inventories/all_inventories.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e5f5bdf45bf57f15c6621c94f3279cf0fe9636bb
--- /dev/null
+++ b/lib/src/screens/home/inventories/all_inventories.dart
@@ -0,0 +1,249 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/appbar/sigapappbar.dart';
+import 'package:mobile_apps/src/repositories/inventory_repository.dart';
+
+// Extension import
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+
+import 'inventory.dart';
+
+class AllInventoriesWidget extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      backgroundColor: ColorConstants.backgroundColor,
+      appBar: SigapAppBar(title: 'Semua Inventaris'),
+      body: Container(
+        padding: EdgeInsets.symmetric(
+            horizontal: MarginConstants.horizontalFromScreen,
+            vertical: MarginConstants.verticalFromScreen),
+        child: _AllInventoryBody(),
+      ),
+    );
+  }
+}
+
+class _AllInventoryBody extends StatefulWidget {
+  @override
+  __AllInventoryBodyState createState() => __AllInventoryBodyState();
+}
+
+class __AllInventoryBodyState extends State<_AllInventoryBody> {
+  InventoryRepository repository;
+  Query inventoriesCollections;
+  String fieldSelected;
+  bool descending;
+
+  @override
+  void initState() {
+    repository = InventoryRepository();
+    inventoriesCollections = repository.inventoriesCollections;
+    descending = true;
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    if (fieldSelected != null) {
+      inventoriesCollections = repository.inventoriesCollections;
+      inventoriesCollections =
+          inventoriesCollections.orderBy(fieldSelected, descending: descending);
+    } else {
+      inventoriesCollections = repository.inventoriesCollections;
+    }
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      mainAxisAlignment: MainAxisAlignment.start,
+      children: <Widget>[
+        Expanded(
+          flex: 1,
+          child: Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            children: <Widget>[
+              Text(
+                'Rekap Siaga Wabah',
+                style: TextStyle(fontWeight: FontWeight.w600, fontSize: 20),
+              ),
+              Row(
+                mainAxisAlignment: MainAxisAlignment.spaceAround,
+                children: <Widget>[
+                  Padding(
+                    padding: const EdgeInsets.only(right: 10),
+                    child: GestureDetector(
+                      onTap: () {
+                        showModalBottomSheet(
+                            context: context,
+                            builder: (context) => sortModal(),
+                            backgroundColor: Colors.transparent,
+                            isScrollControlled: true);
+                      },
+                      child: Row(
+                        children: <Widget>[Icon(Icons.sort), Text('Sort')],
+                      ),
+                    ),
+                  ),
+                  Row(
+                    children: <Widget>[Icon(Icons.filter_list), Text('Filter')],
+                  )
+                ],
+              )
+            ],
+          ),
+        ),
+        Expanded(
+          flex: 10,
+          child: StreamBuilder<QuerySnapshot>(
+            stream: inventoriesCollections.snapshots(),
+            builder: (_, snapshot) {
+              if (snapshot.hasError) {
+                SchedulerBinding.instance.addPostFrameCallback((_) {
+                  Scaffold.of(context).showErrorSnackBar(
+                      'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                });
+              }
+              if (snapshot.connectionState == ConnectionState.waiting) {
+                return Center(child: CircularProgressIndicator());
+              }
+              final inventories = repository.inventories(snapshot);
+              return ListView.builder(
+                  itemCount: inventories.length,
+                  itemBuilder: (BuildContext context, int index) {
+                    return InventoryWidget(inventories[index]);
+                  });
+            },
+          ),
+        )
+      ],
+    );
+  }
+
+  Widget sortModal() {
+    return StatefulBuilder(
+      builder: (context, setStateModal) {
+        return Container(
+          height: MediaQuery.of(context).size.height / 3,
+          padding: EdgeInsets.symmetric(horizontal: 10),
+          decoration: BoxDecoration(
+            color: Colors.white,
+            borderRadius: BorderRadius.only(
+              topLeft: const Radius.circular(25.0),
+              topRight: const Radius.circular(25.0),
+            ),
+          ),
+          child: Column(
+            mainAxisAlignment: MainAxisAlignment.center,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: <Widget>[
+              Row(
+                crossAxisAlignment: CrossAxisAlignment.center,
+                mainAxisAlignment: MainAxisAlignment.center,
+                mainAxisSize: MainAxisSize.min,
+                children: <Widget>[
+                  Expanded(
+                    flex: 2,
+                    child: DropdownButton<String>(
+                        value: fieldSelected,
+                        style: TextStyle(
+                            color: ColorConstants.redPrimary,
+                            fontSize: 18,
+                            fontWeight: FontWeight.w700,
+                            decorationStyle: TextDecorationStyle.solid,
+                            decorationColor: ColorConstants.bluePrimary,
+                            decorationThickness: 2),
+                        icon: Icon(Icons.arrow_downward,
+                            color: ColorConstants.bluePrimary),
+                        underline: Container(
+                          height: 2,
+                          color: ColorConstants.bluePrimary,
+                        ),
+                        items: <String>['terpenuhi', 'kebutuhan']
+                            .map<DropdownMenuItem<String>>(
+                                (String e) => DropdownMenuItem<String>(
+                                      child: Text(
+                                        e.toUpperCase(),
+                                      ),
+                                      value: e,
+                                    ))
+                            .toList(),
+                        hint: Text(
+                          'Sort by',
+                          style: TextStyle(color: ColorConstants.bluePrimary),
+                        ),
+                        iconSize: 24,
+                        onChanged: (val) {
+                          setStateModal(() {
+                            fieldSelected = val;
+                          });
+                          setState(() {
+                            fieldSelected = val;
+                          });
+                        }),
+                  ),
+                  Expanded(
+                    flex: 2,
+                    child: Column(
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      mainAxisAlignment: MainAxisAlignment.center,
+                      children: <Widget>[
+                        RadioListTile<bool>(
+                          title: const Text('Ascending'),
+                          value: false,
+                          groupValue: descending,
+                          onChanged: (bool value) {
+                            setStateModal(() {
+                              descending = value;
+                              fieldSelected = fieldSelected ?? 'kebutuhan';
+                            });
+                            setState(() {
+                              fieldSelected = fieldSelected ?? 'kebutuhan';
+                              descending = value;
+                            });
+                          },
+                        ),
+                        RadioListTile<bool>(
+                          title: const Text('Descending'),
+                          value: true,
+                          groupValue: descending,
+                          onChanged: (bool value) {
+                            setStateModal(() {
+                              descending = value;
+                              fieldSelected = fieldSelected ?? 'kebutuhan';
+                            });
+                            setState(() {
+                              fieldSelected = fieldSelected ?? 'kebutuhan';
+                              descending = value;
+                            });
+                          },
+                        ),
+                      ],
+                    ),
+                  )
+                ],
+              ),
+              SizedBox(
+                  width: double.infinity,
+                  child: RaisedButton(
+                    onPressed: () {
+                      setStateModal(() {
+                        fieldSelected = null;
+                      });
+                      setState(() {
+                        fieldSelected = null;
+                      });
+                    },
+                    child: Text('Clear'),
+                    color: ColorConstants.redPrimary,
+                    textColor: Colors.white,
+                  ))
+            ],
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/src/screens/home/inventories/inventories.dart b/lib/src/screens/home/inventories/inventories.dart
index 4b5cfb4f4fdba7c91c5536fe9bb99d3923a600ca..78c80b277ba324ba674b54b06f34c91a70ebfc70 100644
--- a/lib/src/screens/home/inventories/inventories.dart
+++ b/lib/src/screens/home/inventories/inventories.dart
@@ -1,10 +1,13 @@
+import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/scheduler.dart';
 import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/constants.dart';
 import 'package:mobile_apps/src/repositories/inventory_repository.dart';
 
 // Extension import
 import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+import 'package:mobile_apps/src/routes.dart';
 
 import 'inventory.dart';
 
@@ -16,8 +19,8 @@ class InventoriesWidget extends StatelessWidget {
       crossAxisAlignment: CrossAxisAlignment.start,
       children: <Widget>[
         Text(
-          'Persediaan Siaga Wabah',
-          style: Theme.of(context).textTheme.headline5,
+          'Rekap Kebutuhan',
+          style: TextStyle(fontWeight: FontWeight.w600, fontSize: 20),
         ),
         StreamBuilder(
           stream: repository.inventoriesCollections.snapshots(),
@@ -31,13 +34,21 @@ class InventoriesWidget extends StatelessWidget {
             if (snapshot.connectionState == ConnectionState.waiting) {
               return Center(child: CircularProgressIndicator());
             }
-            final inventories = repository.inventories(snapshot).take(5);
+            final inventories = repository.inventories(snapshot).take(3);
             return Column(
               children: [
                 ...inventories
                     .map((inventory) => InventoryWidget(inventory))
                     .toList(),
-                FlatButton(child: Text('Lihat Semua'), onPressed: () {},),    
+                InkWell(
+                  onTap: () {
+                      Navigator.of(context).pushNamed(AppRoute.recap);
+                    },
+                  child: Text(
+                    'Lihat Semua',
+                    style: TextStyle(color: ColorConstants.redPrimary),
+                  ),
+                ),
               ],
             );
           },
diff --git a/lib/src/screens/home/inventories/inventory.dart b/lib/src/screens/home/inventories/inventory.dart
index 7e52872ffa70ed8a9b831c62febff28b87fe921b..6a8499e43871ce9daa21e6da77b9964c5b9e3744 100644
--- a/lib/src/screens/home/inventories/inventory.dart
+++ b/lib/src/screens/home/inventories/inventory.dart
@@ -1,6 +1,10 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/progressbar/progressbar.dart';
 import 'package:mobile_apps/src/models/inventory.dart';
+import 'package:mobile_apps/src/models/item.dart';
+import 'package:mobile_apps/src/repositories/item_repository.dart';
 
 extension ImageStringExtension on String {
   NetworkImage get networkImage => NetworkImage(this);
@@ -9,17 +13,53 @@ extension ImageStringExtension on String {
 class InventoryWidget extends StatelessWidget {
   final Inventory inventory;
 
+
   const InventoryWidget(this.inventory, {Key key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
-    return Card(
-      child: ListTile(
-        trailing: CircleAvatar(
-          backgroundImage: inventory.imageUrl?.networkImage,
-        ),
-        title: Text(inventory.name),
-        subtitle: Text('${inventory.availableStock}/${inventory.totalStock}'),
+    final itemRepository = ItemRepository();
+    return Container(
+      margin: EdgeInsets.only(bottom: 8),
+      child: Column(
+        children: <Widget>[
+          Container(
+            margin: EdgeInsets.only(bottom: 10),
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: <Widget>[
+                StreamBuilder(
+                  stream: itemRepository.itemDetail(inventory.itemId).snapshots(),
+                  builder: (context, snapshot) {
+                    if (snapshot.hasError) {
+                      return Text('Error has occured');
+                    }
+                    if (snapshot.connectionState == ConnectionState.waiting) {
+                      return Center(child: CircularProgressIndicator());
+                    }
+                    final item = Item.fromJson(snapshot.data.data);
+                    return Text(
+                      item.nama,
+                      style: TextStyle(fontWeight: FontWeight.w600, fontSize: 12),
+                    );
+                  }
+                ),
+                RichText(
+                  text: TextSpan(
+                    text: inventory.terpenuhi.toString() + '/',
+                    style: TextStyle(fontSize: 12, color: ColorConstants.bluePrimary),
+                    children: <TextSpan>[
+                      TextSpan(
+                          text: inventory.kebutuhan.toString(),
+                          style: TextStyle(color: Colors.black)),
+                    ],
+                  ),
+                )
+              ],
+            ),
+          ),
+          ProgressBar(inventory.terpenuhi, inventory.kebutuhan)
+        ],
       ),
     );
   }
diff --git a/lib/src/screens/home/kontak/kontak.dart b/lib/src/screens/home/kontak/kontak.dart
new file mode 100644
index 0000000000000000000000000000000000000000..1fd08982be018ee8ab662982516d4d92cbc5275b
--- /dev/null
+++ b/lib/src/screens/home/kontak/kontak.dart
@@ -0,0 +1,112 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:flutter/widgets.dart';
+import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
+import 'package:mobile_apps/src/common/functions.dart';
+import 'package:mobile_apps/src/components/buttons/buttons.dart';
+import 'package:mobile_apps/src/models/kontak.dart';
+import 'package:mobile_apps/src/repositories/kontak_repository.dart';
+
+class KontakWidget extends StatelessWidget {
+  final String institusi;
+
+  const KontakWidget({this.institusi, Key key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final repository = KontakRepository();
+    final collections = institusi == null
+        ? repository.kontakSigapCollections
+        : repository.kontakCollections(institusi);
+    return Padding(
+        padding: EdgeInsets.symmetric(horizontal: 12, vertical: 16),
+        child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: <Widget>[
+              Text(
+                  'Kontak ${institusi == null ? "Koordinator" : "untuk Donasi"}',
+                  style: Theme.of(context).textTheme.headline6),
+              StreamBuilder(
+                stream: collections.snapshots(),
+                builder: (_, snapshot) {
+                  if (snapshot.hasError) {
+                    SchedulerBinding.instance.addPostFrameCallback((_) {
+                      Text('Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                    });
+                  }
+                  if (snapshot.connectionState == ConnectionState.waiting) {
+                    return Center(child: CircularProgressIndicator());
+                  }
+                  final semuaKontak = repository.semuaKontak(snapshot);
+                  return ListView.builder(
+                    itemBuilder: (BuildContext context, int index) =>
+                        KontakInfo(semuaKontak[index]),
+                    itemCount: semuaKontak.length,
+                    shrinkWrap: true,
+                  );
+                },
+              )
+            ]));
+  }
+}
+
+class KontakInfo extends StatelessWidget {
+  final Kontak kontak;
+
+  const KontakInfo(this.kontak, {Key key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Card(
+      child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: <Widget>[
+            Padding(
+              padding: const EdgeInsets.only(top: 20, left: 16, bottom: 16),
+              child: ListTile(
+                title: Text(
+                    kontak.jabatan != null ? kontak.jabatan.toUpperCase() : '',
+                    style: Theme.of(context).textTheme.headline6),
+                subtitle: Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: <Widget>[
+                    if (kontak.nama != null)
+                      Text(kontak.nama,
+                          style: Theme.of(context).textTheme.subtitle2),
+                  ],
+                ),
+              ),
+            ),
+            Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: <Widget>[
+                if (kontak.telepon != null)
+                  KontakButton(
+                      Icon(Icons.phone_in_talk),
+                      'Telepon ${kontak.telepon}',
+                      () => launchURL(kontak.telepon, scheme: 'tel')),
+                if (kontak.telepon != null)
+                  KontakButton(
+                      Icon(MdiIcons.whatsapp),
+                      'WhatsApp ${kontak.telepon}',
+                      () => launchURL(
+                          '//wa.me/${cleanPhoneNumber(kontak.telepon)}',
+                          scheme: 'https'))
+              ],
+            ),
+            if (kontak.email != null)
+              KontakButton(
+                Icon(Icons.mail_outline),
+                'E-mail ${kontak.email}',
+                () => launchURL(kontak.email, scheme: 'mailto'),
+                mainAxisSize: MainAxisSize.max,
+              )
+          ]),
+    );
+  }
+}
+
+class KontakButton extends IconArrowButton {
+  const KontakButton(icon, text, onPressed, {mainAxisSize})
+      : super(icon, text, onPressed, mainAxisSize: mainAxisSize);
+}
diff --git a/lib/src/screens/homeindustry/home_industry.dart b/lib/src/screens/homeindustry/home_industry.dart
new file mode 100644
index 0000000000000000000000000000000000000000..bb3c12188f4d4fabf6641b3f717d9af66843ba83
--- /dev/null
+++ b/lib/src/screens/homeindustry/home_industry.dart
@@ -0,0 +1,234 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/common/functions.dart';
+import 'package:mobile_apps/src/components/appbar/sigapappbar.dart';
+import 'package:mobile_apps/src/components/buttons/buttons.dart';
+import 'package:mobile_apps/src/repositories/home_industry_repository.dart';
+// Extension import
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+import 'package:mobile_apps/src/screens/home/kontak/kontak.dart';
+import 'package:mobile_apps/src/screens/homeindustry/product/product_home_industry.dart';
+
+class HomeIndustryPage extends StatelessWidget {
+  final String id;
+  HomeIndustryPage({@required this.id, Key key}) : super(key: key);
+  @override
+  Widget build(BuildContext context) {
+    final repository = HomeIndustryRepository();
+    return Scaffold(
+      backgroundColor: ColorConstants.backgroundColor,
+      appBar: SigapAppBar(
+        title: 'Detail Home Industry',
+      ),
+      body: SingleChildScrollView(
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: <Widget>[
+            Container(
+              padding: EdgeInsets.symmetric(vertical: 27, horizontal: 20),
+              margin: EdgeInsets.only(bottom: 28),
+              decoration: BoxDecoration(color: Colors.white),
+              child: StreamBuilder(
+                  stream: repository.getHomeIndustryDocument(id).snapshots(),
+                  builder: (context, snapshot) {
+                    if (snapshot.hasError) {
+                      SchedulerBinding.instance.addPostFrameCallback((_) {
+                        Scaffold.of(context).showErrorSnackBar(
+                            'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                      });
+                    }
+                    if (snapshot.connectionState == ConnectionState.waiting) {
+                      return Center(child: CircularProgressIndicator());
+                    }
+                    final homeIndustry = repository.homeIndustry(snapshot);
+                    return Column(
+                      crossAxisAlignment: CrossAxisAlignment.start,
+                      children: <Widget>[
+                        FittedBox(
+                            fit: BoxFit.fitWidth,
+                            child: Text(
+                              homeIndustry.nama,
+                              style: TextStyle(
+                                  fontSize: 20, fontWeight: FontWeight.bold),
+                            )),
+                        Container(
+                          margin: EdgeInsets.only(bottom: 8),
+                          child: Row(
+                            mainAxisAlignment: MainAxisAlignment.start,
+                            children: <Widget>[
+                              Icon(
+                                Icons.home,
+                                color: ColorConstants.grayPrimary,
+                              ),
+                              Container(
+                                margin: EdgeInsets.only(left: 9),
+                                child: Text(
+                                  'Home Industry',
+                                  style: TextStyle(fontSize: 12),
+                                ),
+                              )
+                            ],
+                          ),
+                        ),
+                        Row(
+                          children: <Widget>[
+                            Expanded(
+                              flex: 2,
+                              child: Text(
+                                homeIndustry.alamat,
+                                style: TextStyle(fontSize: 12),
+                              ),
+                            ),
+                            Expanded(
+                              flex: 1,
+                              child: RoundedButton(
+                                child: Row(
+                                  mainAxisAlignment: MainAxisAlignment.center,
+                                  children: <Widget>[
+                                    Icon(
+                                      Icons.map,
+                                      color: ColorConstants.grayPrimary,
+                                    ),
+                                    Text(
+                                      'Maps',
+                                      style: TextStyle(
+                                          color: ColorConstants.grayPrimary),
+                                    )
+                                  ],
+                                ),
+                                elevation: 0,
+                                radius: 20,
+                                padding: EdgeInsets.symmetric(vertical: 10),
+                                color: Colors.transparent,
+                                borderColor: ColorConstants.grayPrimary,
+                                style: TextStyle(
+                                    color: ColorConstants.grayPrimary),
+                                onPressed: () => launchURL(
+                                    'https://www.google.com/maps/search/?'
+                                    'api=1&query='
+                                    '${homeIndustry.geoLocation['lat']}'
+                                    ',${homeIndustry.geoLocation['long']}'),
+                              ),
+                            )
+                          ],
+                        ),
+                        InkWell(
+                          onTap: () => launchURL(
+                              'https://www.google.com/maps/search/rumah+sakit/@${homeIndustry.geoLocation['lat']},${homeIndustry.geoLocation['long']}'),
+                          child: Text(
+                            'Lihat Rumah Sakit terdekat',
+                            style: TextStyle(color: ColorConstants.redPrimary),
+                          ),
+                        ),
+                        Container(
+                            margin: EdgeInsets.only(top: 7),
+                            child: Text(
+                              'Kontak',
+                              style: TextStyle(
+                                  fontSize: 20, fontWeight: FontWeight.bold),
+                            )),
+                        StreamBuilder(
+                            stream: repository
+                                .getAllKontakHomeIndustry(id)
+                                .snapshots(),
+                            builder: (context, snapshot) {
+                              if (snapshot.hasError) {
+                                SchedulerBinding.instance
+                                    .addPostFrameCallback((_) {
+                                  Scaffold.of(context).showErrorSnackBar(
+                                      'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                                });
+                              }
+                              if (snapshot.connectionState ==
+                                  ConnectionState.waiting) {
+                                return Center(
+                                    child: CircularProgressIndicator());
+                              }
+                              final semuaKontak =
+                                  repository.semuaKontakHomeIndustry(snapshot);
+                              return Column(
+                                children: <Widget>[
+                                  ...semuaKontak.map((kontak) {
+                                    return Column(
+                                      crossAxisAlignment:
+                                          CrossAxisAlignment.start,
+                                      children: <Widget>[
+                                        Text(kontak.nama),
+                                        if (kontak.telepon != null)
+                                          KontakButton(
+                                            Icon(Icons.phone_in_talk),
+                                            'Telepon ${kontak.telepon}',
+                                            () => launchURL(kontak.telepon,
+                                                scheme: 'tel'),
+                                            mainAxisSize: MainAxisSize.max,
+                                          ),
+                                        if (kontak.telepon != null)
+                                          KontakButton(
+                                              Icon(MdiIcons.whatsapp),
+                                              'WhatsApp ${kontak.telepon}',
+                                              () => launchURL(
+                                                    '//wa.me/${cleanPhoneNumber(kontak.telepon)}',
+                                                    scheme: 'https',
+                                                  ),
+                                              mainAxisSize: MainAxisSize.max),
+                                        if (kontak.email != null)
+                                          KontakButton(
+                                            Icon(Icons.mail_outline),
+                                            'E-mail ${kontak.email}',
+                                            () => launchURL(
+                                              kontak.email,
+                                              scheme: 'mailto',
+                                            ),
+                                            mainAxisSize: MainAxisSize.max,
+                                          ),
+                                      ],
+                                    );
+                                  })
+                                ],
+                              );
+                            })
+                      ],
+                    );
+                  }),
+            ),
+            Container(
+              margin: EdgeInsets.symmetric(horizontal: 20),
+              child: StreamBuilder(
+                  stream: repository.getAllProductHomeIndustry(id).snapshots(),
+                  builder: (context, snapshot) {
+                    if (snapshot.hasError) {
+                      SchedulerBinding.instance.addPostFrameCallback((_) {
+                        Scaffold.of(context).showErrorSnackBar(
+                            'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                      });
+                    }
+                    if (snapshot.connectionState == ConnectionState.waiting) {
+                      return Center(child: CircularProgressIndicator());
+                    }
+                    final semuaProduct =
+                        repository.semuaProductHomeIndustry(snapshot);
+                    return Column(
+                      children: <Widget>[
+                        ...semuaProduct.map((product) {
+                          return ProductHomeIndustryWidget(product: product);
+                        })
+                      ],
+                    );
+                  }),
+            ),
+            Container(
+              margin: EdgeInsets.symmetric(horizontal: 20),
+              child: Text(
+                'Produsen dapat memproduksi produk sesuai permintaan jika bahan baku dan dana tersedia, harap hubungi kontak untuk informasi lebih lanjut.',
+                style: TextStyle(fontSize: 12, color: ColorConstants.muteText),
+                textAlign: TextAlign.justify,
+              ),
+            )
+          ],
+        ),
+      ),
+    );
+  }
+}
diff --git a/lib/src/screens/homeindustry/product/product_home_industry.dart b/lib/src/screens/homeindustry/product/product_home_industry.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c9110434e4bddbb5b4a193b5456d60ed404fb10d
--- /dev/null
+++ b/lib/src/screens/homeindustry/product/product_home_industry.dart
@@ -0,0 +1,75 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:intl/intl.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/models/product_home_industry.dart';
+import 'package:mobile_apps/src/repositories/item_repository.dart';
+// Extension import
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+
+class ProductHomeIndustryWidget extends StatelessWidget {
+  final ProductHomeIndustry product;
+
+  ProductHomeIndustryWidget({@required this.product, Key key})
+      : super(key: key);
+  @override
+  Widget build(BuildContext context) {
+    final itemRepository = ItemRepository();
+    return Card(
+      child: Container(
+        margin: EdgeInsets.symmetric(
+            vertical: 23, horizontal: MarginConstants.horizontalFromScreen),
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.spaceAround,
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: <Widget>[
+            Container(
+              margin: EdgeInsets.only(bottom: 7),
+              child: Text(
+              "${NumberFormat.simpleCurrency(locale: 'id').format(product.harga)}/pcs",
+              style: TextStyle(
+                  color: ColorConstants.redSecondary,
+                  fontWeight: FontWeight.bold,
+                  fontSize: 20),
+            ),
+            ),
+            StreamBuilder(
+                stream: itemRepository.itemDocumentFromPath(path: product.idItem).snapshots(),
+                builder: (context, snapshot) {
+                  if (snapshot.hasError) {
+                    SchedulerBinding.instance.addPostFrameCallback((_) {
+                      Scaffold.of(context).showErrorSnackBar(
+                          'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                    });
+                  }
+                  if (snapshot.connectionState == ConnectionState.waiting) {
+                    return Center(child: CircularProgressIndicator());
+                  }
+                  final item = itemRepository.item(snapshot);
+                  return _description('Jenis Produk', '${item.nama} buah');
+                }),
+            _description('Kapasitas Produksi',
+                '${product.kapasitas} pcs/${product.durasi}'),
+            _description('Bahan yang dibutuhkan', '${product.bahan}')
+          ],
+        ),
+      ),
+    );
+  }
+
+  Widget _description(String title, String description) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: <Widget>[
+        Text(
+          title,
+          style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
+        ),
+        Text(
+          description,
+          style: TextStyle(fontSize: 16),
+        )
+      ],
+    );
+  }
+}
diff --git a/lib/src/screens/institusi-screen/institusi_screen.dart b/lib/src/screens/institusi-screen/institusi_screen.dart
new file mode 100644
index 0000000000000000000000000000000000000000..05490df1cdbdded5ae7c2b5604d294cd5530b633
--- /dev/null
+++ b/lib/src/screens/institusi-screen/institusi_screen.dart
@@ -0,0 +1,114 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/appbar/sigapappbar.dart';
+import 'package:mobile_apps/src/components/buttons/buttons.dart';
+import 'package:mobile_apps/src/repositories/institusi_repository.dart';
+import 'package:mobile_apps/src/screens/home/kontak/kontak.dart';
+import 'kebutuhan/kebutuhan.dart';
+// Extension import
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+
+class InstitusiScreen extends StatefulWidget {
+  final String namaInstitusi;
+  final String idInstitusi;
+
+  InstitusiScreen({@required this.namaInstitusi, @required this.idInstitusi});
+
+  @override
+  _InstitusiScreenState createState() => _InstitusiScreenState();
+}
+
+class _InstitusiScreenState extends State<InstitusiScreen> {
+  InstitusiRepository repository = InstitusiRepository();
+  Stream<DocumentSnapshot> _stream;
+  @override
+  void initState() {
+    _stream =
+        InstitusiRepository().institusiDocument(widget.idInstitusi).snapshots();
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      backgroundColor: ColorConstants.backgroundColor,
+      appBar: SigapAppBar(
+        title: 'Rekap Kebutuhan',
+      ),
+      body: SingleChildScrollView(
+        child: Column(
+          children: <Widget>[
+            Container(
+              padding: EdgeInsets.only(left: 37, top: 27, bottom: 27),
+              color: Colors.white,
+              child: StreamBuilder(
+                  stream: _stream,
+                  builder: (context, snapshot) {
+                    if (snapshot.hasError) {
+                      SchedulerBinding.instance.addPostFrameCallback((_) {
+                        Scaffold.of(context).showErrorSnackBar(
+                            'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                      });
+                    }
+                    if (snapshot.connectionState == ConnectionState.waiting) {
+                      return Center(child: CircularProgressIndicator());
+                    }
+                    final institusi = repository.institusi(snapshot);
+                    return Column(
+                      crossAxisAlignment: CrossAxisAlignment.start,
+                      children: <Widget>[
+                        Text(
+                          institusi.nama,
+                          style: TextStyle(
+                              fontWeight: FontWeight.bold, fontSize: 20),
+                        ),
+                        Text(
+                          institusi.alamat,
+                          style: TextStyle(fontSize: 12),
+                        ),
+                        Text(
+                          '${institusi.info1} | ${institusi.info2}',
+                          style: TextStyle(
+                              color: ColorConstants.grayPrimary, fontSize: 12),
+                        ),
+                        RoundedButton(text: 'Subscribe',
+                            elevation: 0.0,
+                            padding: EdgeInsets.symmetric(horizontal: 20, vertical: 11),
+                            radius: 32.0,
+                            style: TextStyle(
+                                color: Colors.white,
+                                fontSize: 12.0,
+                                fontWeight: FontWeight.bold),
+                            onPressed: () {})
+                      ],
+                    );
+                  }),
+            ),
+            Container(
+              padding: EdgeInsets.symmetric(
+                horizontal: MarginConstants.horizontalFromScreen,
+                vertical: MarginConstants.verticalFromScreen,
+              ),
+              child: Column(
+                children: <Widget>[
+                  Kebutuhan(
+                    idInstitusi: widget.idInstitusi,
+                    namaInstitusi: widget.namaInstitusi,
+                  ),
+                  KontakWidget(institusi: widget.idInstitusi)
+                ],
+              ),
+            )
+          ],
+        ),
+      ),
+    );
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+  }
+}
diff --git a/lib/src/screens/institusi-screen/kebutuhan/kebutuhan.dart b/lib/src/screens/institusi-screen/kebutuhan/kebutuhan.dart
new file mode 100644
index 0000000000000000000000000000000000000000..ee068170e1a1c1cce7b68b390356d4c53de493f4
--- /dev/null
+++ b/lib/src/screens/institusi-screen/kebutuhan/kebutuhan.dart
@@ -0,0 +1,145 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/repositories/item_repository.dart';
+import 'package:mobile_apps/src/repositories/kebutuhan_repository.dart';
+import 'package:mobile_apps/src/routes.dart';
+
+class Kebutuhan extends StatelessWidget {
+  final String idInstitusi;
+  final String namaInstitusi;
+  Kebutuhan({@required this.idInstitusi, @required this.namaInstitusi});
+  @override
+  Widget build(BuildContext context) {
+    final kebutuhanRepository = KebutuhanRepository();
+    return Card(
+      color: Colors.white,
+      child: Container(
+        margin: EdgeInsets.only(top: 25, left: 13, bottom: 20, right: 14),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: <Widget>[
+            Container(
+                margin: EdgeInsets.only(bottom: 25),
+                child: Text(
+                  'Kebutuhan $namaInstitusi',
+                  style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
+                )),
+            StreamBuilder(
+              stream: kebutuhanRepository
+                  .kebutuhanCollections(idInstitusi)
+                  .snapshots(),
+              builder: (_, snapshot) {
+                if (snapshot.hasError) {
+                  SchedulerBinding.instance.addPostFrameCallback((_) {
+                    Text('Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                  });
+                }
+                if (snapshot.connectionState == ConnectionState.waiting) {
+                  return Center(child: CircularProgressIndicator());
+                }
+                final allKebutuhan = kebutuhanRepository.kebutuhan(snapshot);
+                return Container(
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: <Widget>[
+                      ...allKebutuhan.map((item) {
+                        return kebutuhanProgress(
+                            idItemNeeded: item.id,
+                            idItem: item.itemPath,
+                            terpenuhi: item.terpenuhi,
+                            kebutuhan: item.kebutuhan,
+                            context: context);
+                      }).toList()
+                    ],
+                  ),
+                );
+              },
+            )
+          ],
+        ),
+      ),
+    );
+  }
+
+  Widget kebutuhanProgress(
+      {@required String idItemNeeded,
+      @required String idItem,
+      @required int kebutuhan,
+      @required int terpenuhi,
+      @required BuildContext context}) {
+    final itemRepository = ItemRepository();
+    return InkWell(
+      onTap: () {
+        Navigator.of(context).pushNamed(AppRoute.itemNeeded(
+          idInstitusi: idInstitusi,
+          idItemNeeded: idItemNeeded,
+          namaInstitusi: namaInstitusi,
+        ));
+      },
+      child: Column(
+        children: <Widget>[
+          Divider(
+            color: ColorConstants.grayPrimary.withOpacity(0.15),
+            thickness: 1,
+            height: 1,
+          ),
+          Container(
+            margin: EdgeInsets.only(left: 7, top: 12, bottom: 12),
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: <Widget>[
+                StreamBuilder(
+                    stream: itemRepository
+                        .itemDocumentFromPath(path: idItem)
+                        .snapshots(),
+                    builder: (_, snapshot) {
+                      if (snapshot.hasError) {
+                        SchedulerBinding.instance.addPostFrameCallback((_) {
+                          Text(
+                              'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                        });
+                      }
+                      if (snapshot.connectionState == ConnectionState.waiting) {
+                        return Center(child: CircularProgressIndicator());
+                      }
+                      final item = itemRepository.item(snapshot);
+                      return Text(
+                        item.nama ?? '',
+                        style: TextStyle(fontSize: 14),
+                        overflow: TextOverflow.ellipsis,
+                      );
+                    }),
+                Row(
+                  children: <Widget>[
+                    Container(
+                      margin: EdgeInsets.only(right: 16),
+                      child: RichText(
+                        overflow: TextOverflow.ellipsis,
+                        text: TextSpan(
+                          text: terpenuhi.toString() + '/',
+                          style: TextStyle(
+                              fontSize: 14, color: ColorConstants.redPrimary),
+                          children: <TextSpan>[
+                            TextSpan(
+                                text: kebutuhan.toString(),
+                                style: TextStyle(color: Colors.black)),
+                          ],
+                        ),
+                      ),
+                    ),
+                    Icon(
+                      Icons.arrow_forward_ios,
+                      color: ColorConstants.grayPrimary.withOpacity(0.3),
+                      size: 20,
+                    )
+                  ],
+                ),
+              ],
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/src/screens/ketersediaan_screen/ketersediaan_agregrasi.dart b/lib/src/screens/ketersediaan_screen/ketersediaan_agregrasi.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b7825d8bdfde7c5a30652008aa69653488bd302e
--- /dev/null
+++ b/lib/src/screens/ketersediaan_screen/ketersediaan_agregrasi.dart
@@ -0,0 +1,384 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/card_item_supplier/card_item_supplier.dart';
+import 'package:mobile_apps/src/components/input/input_search.dart';
+import 'package:mobile_apps/src/models/item_supplier_aggregate.dart';
+import 'package:mobile_apps/src/repositories/item_ketersediaan_aggregate_repository.dart';
+import 'package:mobile_apps/src/repositories/item_repository.dart';
+import 'package:mobile_apps/src/repositories/product_supplier_repository.dart';
+import 'package:mobile_apps/src/repositories/supplier_repository.dart';
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+import 'package:flutter/foundation.dart' show kIsWeb;
+import 'package:mobile_apps/src/routes.dart';
+
+class KetersediaanAgregrasi extends StatefulWidget {
+  final fillPercent = 65.23;
+
+  const KetersediaanAgregrasi({Key key}) : super(key: key);
+  @override
+  _KetersediaanAgregrasiState createState() => _KetersediaanAgregrasiState();
+}
+
+class _KetersediaanAgregrasiState extends State<KetersediaanAgregrasi> {
+  String _search;
+  bool descending;
+  String fieldSelected;
+  ItemKetersediaanRepository ketersediaanAgregrasiRepo;
+  Query ketersediaanAgregrasiCollections;
+
+  Stream<QuerySnapshot> result;
+
+  final List<String> field = ['nama_item', 'nama_supplier', 'stok', 'harga'];
+
+  @override
+  void initState() {
+    _search = '';
+    ketersediaanAgregrasiRepo = ItemKetersediaanRepository();
+    ketersediaanAgregrasiCollections =
+        ketersediaanAgregrasiRepo.itemCollections;
+    descending = true;
+    result = ketersediaanAgregrasiCollections.snapshots();
+    super.initState();
+  }
+
+  void onChange(String s) {
+    setState(() {
+      _search = s;
+    });
+  }
+
+  void onChangeQuery() {
+    if (fieldSelected != null) {
+      ketersediaanAgregrasiCollections =
+          ketersediaanAgregrasiRepo.itemCollections;
+      ketersediaanAgregrasiCollections = ketersediaanAgregrasiCollections
+          .orderBy(fieldSelected, descending: descending);
+    } else {
+      ketersediaanAgregrasiCollections =
+          ketersediaanAgregrasiRepo.itemCollections;
+    }
+    result = ketersediaanAgregrasiCollections.snapshots();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final fillStop = (100 - widget.fillPercent) / 100;
+    return Container(
+      padding: EdgeInsets.only(
+          left: MarginConstants.horizontalFromScreen + 5,
+          right: MarginConstants.horizontalFromScreen + 5,
+          top: MarginConstants.verticalFromScreen + 5),
+      decoration: BoxDecoration(
+          gradient: LinearGradient(colors: [
+        ColorConstants.redSecondary,
+        ColorConstants.redSecondary,
+        Colors.white,
+        Colors.white
+      ], stops: [
+        0.0,
+        fillStop,
+        fillStop,
+        1
+      ], end: Alignment.bottomCenter, begin: Alignment.topCenter)),
+      child: Column(
+        children: <Widget>[
+          Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            children: <Widget>[
+              Text(
+                'Produk Tersedia',
+                style: TextStyle(
+                    color: Colors.white,
+                    fontWeight: FontWeight.w800,
+                    fontSize: 18),
+              ),
+              Row(
+                mainAxisAlignment: MainAxisAlignment.end,
+                children: <Widget>[
+                  IconButton(
+                    icon: Icon(Icons.sort),
+                    onPressed: () {
+                      showModalBottomSheet(
+                          context: context,
+                          builder: (context) => sortModal(),
+                          backgroundColor: Colors.transparent,
+                          isScrollControlled: true);
+                    },
+                    color: Colors.white,
+                  ),
+                  IconButton(
+                    icon: Icon(Icons.filter_list),
+                    onPressed: () {},
+                    color: Colors.white,
+                  ),
+                ],
+              ),
+            ],
+          ),
+          InputSearch(
+            onChange: onChange,
+          ),
+          Expanded(
+            child: Container(
+              constraints: kIsWeb ? BoxConstraints(maxWidth: 600.0) : null,
+              margin: kIsWeb
+                  ? EdgeInsets.symmetric(vertical: 20)
+                  : EdgeInsets.only(top: 20),
+              color: Colors.transparent,
+              child: StreamBuilder<QuerySnapshot>(
+                  stream: result,
+                  builder: (context, snapshot) {
+                    if (snapshot.hasError) {
+                      SchedulerBinding.instance.addPostFrameCallback((_) {
+                        Scaffold.of(context).showErrorSnackBar(
+                            'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                      });
+                    }
+                    ;
+                    if (snapshot.connectionState == ConnectionState.waiting) {
+                      return Center(child: CircularProgressIndicator());
+                    }
+                    ;
+                    final items = _search == ''
+                        ? ketersediaanAgregrasiRepo.allItemAvailable(snapshot)
+                        : ketersediaanAgregrasiRepo.allItemSearch(
+                            _search, snapshot);
+                    return ListView.builder(
+                      itemBuilder: (BuildContext context, int index) {
+                        return _ItemKetersediaan(
+                          itemSupplierAggregate: items[index],
+                        );
+                      },
+                      itemCount: items.length,
+                      shrinkWrap: true,
+                    );
+                  }),
+            ),
+          )
+        ],
+      ),
+    );
+  }
+
+  Widget sortModal() {
+    return StatefulBuilder(
+      builder: (context, setStateModal) {
+        return Container(
+          height: MediaQuery.of(context).size.height / 3,
+          padding: EdgeInsets.symmetric(horizontal: 10),
+          decoration: BoxDecoration(
+            color: Colors.white,
+            borderRadius: BorderRadius.only(
+              topLeft: const Radius.circular(25.0),
+              topRight: const Radius.circular(25.0),
+            ),
+          ),
+          child: Column(
+            mainAxisAlignment: MainAxisAlignment.center,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: <Widget>[
+              Row(
+                crossAxisAlignment: CrossAxisAlignment.center,
+                mainAxisAlignment: MainAxisAlignment.center,
+                mainAxisSize: MainAxisSize.min,
+                children: <Widget>[
+                  Expanded(
+                    flex: 2,
+                    child: DropdownButton<String>(
+                        value: fieldSelected,
+                        style: TextStyle(
+                            color: ColorConstants.redPrimary,
+                            fontSize: 18,
+                            fontWeight: FontWeight.w700,
+                            decorationStyle: TextDecorationStyle.solid,
+                            decorationColor: ColorConstants.bluePrimary,
+                            decorationThickness: 2),
+                        icon: Icon(Icons.arrow_downward,
+                            color: ColorConstants.bluePrimary),
+                        underline: Container(
+                          height: 2,
+                          color: ColorConstants.bluePrimary,
+                        ),
+                        items: field
+                            .map<DropdownMenuItem<String>>(
+                                (String e) => DropdownMenuItem<String>(
+                                      child: Text(
+                                        e.toUpperCase(),
+                                      ),
+                                      value: e,
+                                    ))
+                            .toList(),
+                        hint: Text(
+                          'Sort by',
+                          style: TextStyle(color: ColorConstants.bluePrimary),
+                        ),
+                        iconSize: 24,
+                        onChanged: (val) {
+                          setStateModal(() {
+                            fieldSelected = val;
+                          });
+                          setState(() {
+                            fieldSelected = val;
+                            onChangeQuery();
+                          });
+                        }),
+                  ),
+                  Expanded(
+                    flex: 2,
+                    child: Column(
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      mainAxisAlignment: MainAxisAlignment.center,
+                      children: <Widget>[
+                        RadioListTile<bool>(
+                          title: const Text('Ascending'),
+                          value: false,
+                          groupValue: descending,
+                          onChanged: (bool value) {
+                            setStateModal(() {
+                              descending = value;
+                              fieldSelected = fieldSelected ?? field[0];
+                            });
+                            setState(() {
+                              fieldSelected = fieldSelected ?? field[0];
+                              descending = value;
+                              onChangeQuery();
+                            });
+                          },
+                        ),
+                        RadioListTile<bool>(
+                          title: const Text('Descending'),
+                          value: true,
+                          groupValue: descending,
+                          onChanged: (bool value) {
+                            setStateModal(() {
+                              descending = value;
+                              fieldSelected = fieldSelected ?? field[0];
+                            });
+                            setState(() {
+                              fieldSelected = fieldSelected ?? field[0];
+                              descending = value;
+                              onChangeQuery();
+                            });
+                          },
+                        ),
+                      ],
+                    ),
+                  )
+                ],
+              ),
+              SizedBox(
+                  width: double.infinity,
+                  child: RaisedButton(
+                    onPressed: () {
+                      setStateModal(() {
+                        fieldSelected = null;
+                      });
+                      setState(() {
+                        fieldSelected = null;
+                        onChangeQuery();
+                      });
+                    },
+                    child: Text('Clear'),
+                    color: ColorConstants.redPrimary,
+                    textColor: Colors.white,
+                  ))
+            ],
+          ),
+        );
+      },
+    );
+  }
+}
+
+class _ItemKetersediaan extends StatelessWidget {
+  final ItemSupplierAggregate itemSupplierAggregate;
+
+  const _ItemKetersediaan({Key key, @required this.itemSupplierAggregate})
+      : assert(itemSupplierAggregate != null),
+        super(key: key);
+  @override
+  Widget build(BuildContext context) {
+    final supplierRepo = SupplierRepository();
+    return SizedBox(
+      height: MediaQuery.of(context).size.height / 7,
+      child: StreamBuilder<DocumentSnapshot>(
+          stream: supplierRepo
+              .supplierDocumentFromPath(
+                  path: itemSupplierAggregate.supplierPath)
+              .snapshots(),
+          builder: (context, snapshot) {
+            if (snapshot.hasError) {
+              SchedulerBinding.instance.addPostFrameCallback((_) {
+                Scaffold.of(context).showErrorSnackBar(
+                    'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+              });
+            }
+            ;
+            if (snapshot.connectionState == ConnectionState.waiting) {
+              return Center(child: CircularProgressIndicator());
+            }
+            ;
+            final supplier = supplierRepo.supplier(snapshot);
+            final productFromSuppRepo = ProductSupplierRepository();
+            return StreamBuilder<DocumentSnapshot>(
+                stream: productFromSuppRepo
+                    .oneProductFromSupplierPath(
+                        path: itemSupplierAggregate.barangPath)
+                    .snapshots(),
+                builder: (context, snapshot) {
+                  if (snapshot.hasError) {
+                    SchedulerBinding.instance.addPostFrameCallback((_) {
+                      Scaffold.of(context).showErrorSnackBar(
+                          'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                    });
+                  }
+                  ;
+                  if (snapshot.connectionState == ConnectionState.waiting) {
+                    return Center(child: CircularProgressIndicator());
+                  }
+                  ;
+                  final productSupplier = productFromSuppRepo.product(snapshot);
+                  final itemRepo = ItemRepository();
+                  return StreamBuilder<DocumentSnapshot>(
+                      stream: itemRepo
+                          .itemDocumentFromPath(path: productSupplier.idItem)
+                          .snapshots(),
+                      builder: (context, snapshot) {
+                        if (snapshot.hasError) {
+                          SchedulerBinding.instance.addPostFrameCallback((_) {
+                            Scaffold.of(context).showErrorSnackBar(
+                                'Oops! Ada sesuatu yang salah, mohon coba lagi '
+                                'ya.');
+                          });
+                        }
+                        ;
+                        if (snapshot.connectionState ==
+                            ConnectionState.waiting) {
+                          return Center(child: CircularProgressIndicator());
+                        }
+                        ;
+                        final item = itemRepo.item(snapshot);
+                        return GestureDetector(
+                          onTap: () {
+                            Navigator.of(context).pushNamed(
+                                AppRoute.detailProductSupplier(
+                                    idSupplier: supplier.id,
+                                    idBarang: productSupplier.id));
+                          },
+                          child: CardItemSupplier(
+                            harga: productSupplier.harga,
+                            itemName: item.nama,
+                            stock: productSupplier.stok,
+                            supplierName: supplier.nama,
+                            itemImage: item.url,
+                          ),
+                        );
+                      });
+                });
+          }),
+    );
+  }
+}
diff --git a/lib/src/screens/login/login_screen.dart b/lib/src/screens/login/login_screen.dart
index a647a594145ff6ed0debc095bc24e825e005c261..9e3d1b778d012f9d094cd3e018ef753b558982f1 100644
--- a/lib/src/screens/login/login_screen.dart
+++ b/lib/src/screens/login/login_screen.dart
@@ -33,7 +33,7 @@ class _LoginScreenState extends State<LoginScreen> {
       setState(() => isLoading = false);
     }
     if (authService.isAuthenticated) {
-      await Navigator.of(context).pushNamedAndRemoveUntil(AppRoutes.home.route, (_) => false);
+      await Navigator.of(context).pushNamedAndRemoveUntil(AppRoute.home, (_) => false);
     }
   }
 
diff --git a/lib/src/screens/notification/notification_screen.dart b/lib/src/screens/notification/notification_screen.dart
new file mode 100644
index 0000000000000000000000000000000000000000..ad834bffeafa9933d73f517a186dd621d9d93c0a
--- /dev/null
+++ b/lib/src/screens/notification/notification_screen.dart
@@ -0,0 +1,56 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/appbar/sigapappbar.dart';
+import 'package:mobile_apps/src/repositories/notifikasi_repository.dart';
+import 'package:mobile_apps/src/screens/notification/notifikasi.dart';
+
+// Extension import
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+
+class NotificationScreen extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    final repository = NotifikasiRepository();
+    return Scaffold(
+      backgroundColor: ColorConstants.backgroundColor,
+      appBar: SigapAppBar(title: 'Notifikasi'),
+      body: Container(
+        padding: EdgeInsets.symmetric(
+          horizontal: MarginConstants.horizontalFromScreen,
+          vertical: MarginConstants.verticalFromScreen
+        ),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          mainAxisAlignment: MainAxisAlignment.start,
+          children: <Widget>[
+            Expanded(
+              child: StreamBuilder(
+                stream: repository.notifikasiCollections.orderBy('waktu', descending: true).snapshots(),
+                builder: (_, snapshot) {
+                  if (snapshot.hasError) {
+                    SchedulerBinding.instance.addPostFrameCallback((_) {
+                      Scaffold.of(context).showErrorSnackBar(
+                          'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                    });
+                  }
+                  if (snapshot.connectionState == ConnectionState.waiting) {
+                    return Center(child: CircularProgressIndicator());
+                  }
+                  final semuaNotifikasi = repository.semuaNotifikasi(snapshot);
+                  return ListView.builder(
+                    itemCount: semuaNotifikasi.length,
+                    itemBuilder: (BuildContext context, int index) {
+                      return NotifikasiWidget(semuaNotifikasi[index]);
+                    }
+                  );
+                },
+              ),
+            )
+          ],
+        ),
+      ),
+    );
+  }
+}
diff --git a/lib/src/screens/notification/notifikasi.dart b/lib/src/screens/notification/notifikasi.dart
new file mode 100644
index 0000000000000000000000000000000000000000..2381773b19e48e49c8b42b1c006b9d4967908009
--- /dev/null
+++ b/lib/src/screens/notification/notifikasi.dart
@@ -0,0 +1,48 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:intl/intl.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/models/notifikasi.dart';
+
+
+class NotifikasiWidget extends StatelessWidget {
+  final Notifikasi notifikasi;
+
+  const NotifikasiWidget(this.notifikasi, {Key key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final dateFormat = DateFormat.yMMMMd().add_jm();
+    return Card(
+      child: Padding(
+        padding: EdgeInsets.symmetric(horizontal: MarginConstants.horizontalFromScreen),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          mainAxisSize: MainAxisSize.min,
+          children: <Widget>[
+            Padding(
+              padding: EdgeInsetsDirectional.only(top: 10.0),
+              child: Text(
+                '${dateFormat.format(notifikasi.waktu)}',
+                style: Theme.of(context).textTheme.subtitle2)
+            ),
+            Text(
+              '${notifikasi.judul}',
+              style: Theme.of(context)
+                  .textTheme
+                  .headline6
+                  .copyWith(fontWeight: FontWeight.bold)
+            ),
+            Padding(
+              padding: EdgeInsetsDirectional.only(bottom: 10),
+              child: Text(
+                '${notifikasi.deskripsi}',
+                style: Theme.of(context).textTheme.subtitle1
+              ),
+            ),
+          ]
+        )
+      )
+    );
+  }
+}
diff --git a/lib/src/screens/supplier/all_suppliers.dart b/lib/src/screens/supplier/all_suppliers.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c306a9c8cfce5a70150aa232e47fe08b3b9b04cf
--- /dev/null
+++ b/lib/src/screens/supplier/all_suppliers.dart
@@ -0,0 +1,107 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/appbar/sigapappbar.dart';
+
+// Extension import
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+import 'package:mobile_apps/src/repositories/supplier_repository.dart';
+import 'package:mobile_apps/src/screens/supplier/supplier.dart';
+
+class AllSuppliersWidget extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      backgroundColor: ColorConstants.backgroundColor,
+      appBar: SigapAppBar(title: 'Semua Supplier'),
+      body: ListView(
+        scrollDirection: Axis.vertical,
+        padding: EdgeInsets.symmetric(
+            horizontal: MarginConstants.horizontalFromScreen,
+            vertical: MarginConstants.verticalFromScreen),
+        children: <Widget>[
+          _AllSuppliersBody(),
+        ],
+      ),
+    );
+  }
+}
+
+class _AllSuppliersBody extends StatefulWidget {
+  @override
+  _AllSuppliersBodyState createState() => _AllSuppliersBodyState();
+}
+
+class _AllSuppliersBodyState extends State<_AllSuppliersBody> {
+  List semuaSupplier = [];
+  List currentSearch;
+
+  void updateSearch(String text) {
+    var tempList = semuaSupplier.where((supplier) {
+      var search =
+          supplier.nama.toLowerCase() + supplier.alamat.toLowerCase();
+      return search.contains(text.toLowerCase());
+    });
+    setState(() {
+      currentSearch = List.from(tempList);
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final repository = SupplierRepository();
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: <Widget>[
+        Padding(
+            padding: EdgeInsets.only(bottom: 12.0),
+            child: Text(
+              'Supplier',
+              style: Theme.of(context).textTheme.headline5,
+            )),
+        Padding(
+            padding: EdgeInsets.only(bottom: 12.0),
+            child: Text(
+              'Supplier adalah penyedia bahan dan alat medis ready stock. Lihat daftar supplier dan daftar produk yang mereka jual disini.',
+              style: Theme.of(context).textTheme.caption,
+            )),
+        Padding(
+            padding: EdgeInsets.only(bottom: 12.0),
+            child: TextField(
+              onChanged: updateSearch,
+              decoration: InputDecoration(
+                  labelText: 'Cari nama supplier',
+                  hintText: 'Cari nama supplier',
+                  prefixIcon: Icon(Icons.search),
+                  border: OutlineInputBorder(
+                      borderRadius: BorderRadius.all(Radius.circular(25.0)))),
+            )),
+        StreamBuilder(
+          stream: repository.supplierCollections.snapshots(),
+          builder: (_, snapshot) {
+            if (snapshot.hasError) {
+              SchedulerBinding.instance.addPostFrameCallback((_) {
+                Scaffold.of(context).showErrorSnackBar(
+                    'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+              });
+            }
+            if (snapshot.connectionState == ConnectionState.waiting) {
+              return Center(child: CircularProgressIndicator());
+            }
+            semuaSupplier =
+                repository.semuaSupplier(snapshot).toList();
+            var shown = currentSearch ?? semuaSupplier;
+            return Column(
+              children: [
+                ...shown
+                    .map((supplier) => SupplierWidget(supplier: supplier))
+                    .toList(),
+              ],
+            );
+          },
+        ),
+      ],
+    );
+  }
+}
diff --git a/lib/src/screens/supplier/detailsupplier.dart b/lib/src/screens/supplier/detailsupplier.dart
new file mode 100644
index 0000000000000000000000000000000000000000..8e7e11687bd31a952c94822d96da197bcd54a748
--- /dev/null
+++ b/lib/src/screens/supplier/detailsupplier.dart
@@ -0,0 +1,169 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/common/functions.dart';
+import 'package:mobile_apps/src/components/appbar/sigapappbar.dart';
+import 'package:mobile_apps/src/components/buttons/buttons.dart';
+import 'package:mobile_apps/src/repositories/supplier_repository.dart';
+import 'package:mobile_apps/src/screens/home/kontak/kontak.dart';
+// Extension import
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+import 'package:mobile_apps/src/screens/supplier/tabs.dart';
+
+class DetailSupplier extends StatefulWidget {
+  final String idSupplier;
+  // final String url;
+
+  DetailSupplier({
+    @required this.idSupplier,
+    // @required this.url
+  });
+
+  @override
+  _DetailSupplierState createState() => _DetailSupplierState();
+}
+
+class _DetailSupplierState extends State<DetailSupplier> {
+  @override
+  Widget build(BuildContext context) {
+    final repository = SupplierRepository();
+    return Scaffold(
+      backgroundColor: ColorConstants.backgroundColor,
+      appBar: SigapAppBar(
+        title: 'Detail Supplier',
+      ),
+      body: ListView(
+        shrinkWrap: true,
+        children: <Widget>[
+          StreamBuilder(
+              stream: repository
+                  .supplierDocument(idSupplier: widget.idSupplier)
+                  .snapshots(),
+              builder: (context, snapshot) {
+                if (snapshot.hasError) {
+                  SchedulerBinding.instance.addPostFrameCallback((_) {
+                    Scaffold.of(context).showErrorSnackBar(
+                        'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                  });
+                }
+                if (snapshot.connectionState == ConnectionState.waiting) {
+                  return Center(child: CircularProgressIndicator());
+                }
+                final supplier = repository.supplier(snapshot);
+                return Container(
+                    child: Padding(
+                  padding: const EdgeInsets.symmetric(
+                      horizontal: MarginConstants.horizontalFromScreen,
+                      vertical: 20),
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: <Widget>[
+                      Padding(
+                        padding: const EdgeInsets.symmetric(vertical: 8.0),
+                        child: Text(supplier.nama,
+                            style: TextStyle(
+                                fontWeight: FontWeight.bold, fontSize: 20)),
+                      ),
+                      Row(
+                        mainAxisAlignment: MainAxisAlignment.start,
+                        children: <Widget>[
+                          Icon(
+                            Icons.store,
+                            color: Colors.black,
+                          ),
+                          Padding(
+                            padding: EdgeInsets.only(left: 4),
+                            child: Text('Perusahaan Terbuka'),
+                          )
+                        ],
+                      ),
+                      Row(
+                        children: <Widget>[
+                          Expanded(
+                            flex: 2,
+                            child: Text(
+                              supplier.alamat,
+                              style: TextStyle(fontSize: 12),
+                            ),
+                          ),
+                          Expanded(
+                            flex: 1,
+                            child: RoundedButton(
+                              child: Row(
+                                mainAxisAlignment: MainAxisAlignment.center,
+                                children: <Widget>[
+                                  Icon(
+                                    Icons.map,
+                                    color: ColorConstants.grayPrimary,
+                                  ),
+                                  Text(
+                                    'Maps',
+                                    style: TextStyle(
+                                        color: ColorConstants.grayPrimary),
+                                  )
+                                ],
+                              ),
+                              elevation: 0,
+                              radius: 20,
+                              padding: EdgeInsets.symmetric(vertical: 10),
+                              color: Colors.transparent,
+                              borderColor: ColorConstants.grayPrimary,
+                              style:
+                                  TextStyle(color: ColorConstants.grayPrimary),
+                              onPressed: () => launchURL(
+                                  'https://www.google.com/maps/search/?'
+                                  'api=1&query='
+                                  '${supplier.geoLocation['lat']}'
+                                  ',${supplier.geoLocation['long']}'),
+                            ),
+                          )
+                        ],
+                      ),
+                      InkWell(
+                        onTap: () => launchURL(
+                            'https://www.google.com/maps/search/rumah+sakit/@${supplier.geoLocation['lat']},${supplier.geoLocation['long']}'),
+                        child: Text(
+                          'Lihat Rumah Sakit terdekat',
+                          style: TextStyle(color: ColorConstants.redPrimary),
+                        ),
+                      ),
+                      Padding(
+                        padding: const EdgeInsets.only(top: 16.0),
+                        child: Text(
+                          'Kontak',
+                          style: TextStyle(
+                              fontSize: 16, fontWeight: FontWeight.w600),
+                        ),
+                      ),
+                      Padding(
+                        padding: const EdgeInsets.symmetric(vertical: 8.0),
+                        child: Text(supplier.sales),
+                      ),
+                      if (supplier.telepon != null)
+                        KontakButton(
+                            Icon(Icons.phone_in_talk),
+                            'Telepon ${supplier.telepon}',
+                            () => launchURL(supplier.telepon, scheme: 'tel')),
+                      if (supplier.telepon != null)
+                        KontakButton(
+                            Icon(MdiIcons.whatsapp),
+                            'WhatsApp ${supplier.telepon}',
+                            () => launchURL(
+                                  '//wa.me/${cleanPhoneNumber(supplier.telepon)}',
+                                  scheme: 'https',
+                                )),
+                      Padding(
+                          padding: EdgeInsets.only(top: 32.0),
+                          child: SupplierTabs(
+                              idSupplier: widget.idSupplier,
+                              namaSupplier: supplier.nama)),
+                    ],
+                  ),
+                ));
+              }),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/src/screens/supplier/dokumen.dart b/lib/src/screens/supplier/dokumen.dart
new file mode 100644
index 0000000000000000000000000000000000000000..552cc69dbd2c1d6880031e79bebde4399e657c24
--- /dev/null
+++ b/lib/src/screens/supplier/dokumen.dart
@@ -0,0 +1,64 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/functions.dart';
+import 'package:mobile_apps/src/components/buttons/buttons.dart';
+import 'package:mobile_apps/src/repositories/dokumen_repository.dart';
+
+class ListDokumenWidget extends StatelessWidget {
+  final String idSupplier;
+
+  const ListDokumenWidget(this.idSupplier, {Key key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final repository = DokumenRepository();
+    final snapshots = repository.dokumenSnapshots(idSupplier);
+    return Column(crossAxisAlignment: CrossAxisAlignment.start, children: <
+        Widget>[
+      Padding(
+          padding: EdgeInsets.only(bottom: 12.0),
+          child: Text(
+            'Dokumen Pendukung',
+            style: Theme.of(context)
+                .textTheme
+                .headline5
+                .copyWith(color: Colors.white),
+          )),
+      Flexible(
+          child: Card(
+              child: StreamBuilder(
+                  stream: snapshots,
+                  builder: (_, snapshot) {
+                    if (snapshot.hasError) {
+                      SchedulerBinding.instance.addPostFrameCallback((_) {
+                        Text(
+                            'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                      });
+                    }
+                    if (snapshot.connectionState == ConnectionState.waiting) {
+                      return Center(child: CircularProgressIndicator());
+                    }
+                    final allDokumen = repository.allDokumen(snapshot);
+                    if (allDokumen.isEmpty) {
+                      return Container(
+                          width: double.infinity,
+                          padding: EdgeInsets.all(12.0),
+                          child: Text('Belum ada dokumen.',
+                              style: Theme.of(context).textTheme.subtitle1));
+                    }
+                    return ListView.builder(
+                      itemBuilder: ((context, index) {
+                        final dokumen = allDokumen[index];
+                        return IconArrowButton(
+                            null, dokumen.nama, () => launchURL(dokumen.link),
+                            mainAxisSize: MainAxisSize.max,
+                            style: TextStyle(color: Colors.black));
+                      }),
+                      itemCount: allDokumen.length,
+                      shrinkWrap: true,
+                    );
+                  })))
+    ]);
+  }
+}
diff --git a/lib/src/screens/supplier/list_produk.dart b/lib/src/screens/supplier/list_produk.dart
new file mode 100644
index 0000000000000000000000000000000000000000..f5d9af0f8e9789bfafb04ca3a0f8bc9ec968348e
--- /dev/null
+++ b/lib/src/screens/supplier/list_produk.dart
@@ -0,0 +1,314 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/card_item_supplier/card_item_supplier.dart';
+import 'package:mobile_apps/src/components/input/input_search.dart';
+import 'package:mobile_apps/src/models/product_supplier.dart';
+import 'package:mobile_apps/src/repositories/item_repository.dart';
+import 'package:mobile_apps/src/repositories/product_supplier_repository.dart';
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+import 'package:flutter/foundation.dart' show kIsWeb;
+import 'package:mobile_apps/src/routes.dart';
+
+class ListProdukWidget extends StatefulWidget {
+  final String idSupplier;
+  final String namaSupplier;
+
+  const ListProdukWidget({Key key, @required this.idSupplier, @required this.namaSupplier})
+      : super(key: key);
+  @override
+  _ListProdukWidgetState createState() => _ListProdukWidgetState();
+}
+
+class _ListProdukWidgetState extends State<ListProdukWidget> {
+  String _search;
+  bool descending;
+  String fieldSelected;
+  ProductSupplierRepository barangSupplierRepo;
+  Query barangSupplierCollections;
+
+  final List<String> field = ['stok', 'harga'];
+
+  @override
+  void initState() {
+    _search = '';
+    barangSupplierRepo = ProductSupplierRepository();
+    barangSupplierCollections = barangSupplierRepo.allProductsFromSupplier(
+        idSupplier: widget.idSupplier);
+    descending = true;
+    super.initState();
+  }
+
+  void onChange(String s) {
+    setState(() {
+      _search = s;
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    if (fieldSelected != null) {
+      barangSupplierCollections = barangSupplierRepo.allProductsFromSupplier(
+          idSupplier: widget.idSupplier);
+      barangSupplierCollections = barangSupplierCollections
+          .orderBy(fieldSelected, descending: descending);
+    } else {
+      barangSupplierCollections = barangSupplierRepo.allProductsFromSupplier(
+          idSupplier: widget.idSupplier);
+    }
+    return Column(
+        children: <Widget>[
+          Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            children: <Widget>[
+              Text(
+                'Produk Tersedia',
+                style: Theme.of(context).textTheme.headline5.copyWith(color: Colors.white)
+              ),
+              Row(
+                mainAxisAlignment: MainAxisAlignment.end,
+                children: <Widget>[
+                  IconButton(
+                    icon: Icon(Icons.sort),
+                    onPressed: () {
+                      showModalBottomSheet(
+                          context: context,
+                          builder: (context) => sortModal(),
+                          backgroundColor: Colors.transparent,
+                          isScrollControlled: true);
+                    },
+                    color: Colors.white,
+                  ),
+                  IconButton(
+                    icon: Icon(Icons.filter_list),
+                    onPressed: () {},
+                    color: Colors.white,
+                  ),
+                ],
+              ),
+            ],
+          ),
+          InputSearch(
+            onChange: onChange,
+          ),
+          Expanded(
+            child: Container(
+              constraints: kIsWeb ? BoxConstraints(maxWidth: 600.0) : null,
+              margin: kIsWeb
+                  ? EdgeInsets.symmetric(vertical: 20)
+                  : EdgeInsets.only(top: 20),
+              color: Colors.transparent,
+              child: StreamBuilder<QuerySnapshot>(
+                  stream: barangSupplierCollections.snapshots(),
+                  builder: (context, snapshot) {
+                    if (snapshot.hasError) {
+                      SchedulerBinding.instance.addPostFrameCallback((_) {
+                        Scaffold.of(context).showErrorSnackBar(
+                            'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                      });
+                    }
+                    if (snapshot.connectionState == ConnectionState.waiting) {
+                      return Center(child: CircularProgressIndicator());
+                    }
+                    final items = barangSupplierRepo.allProducts(snapshot);
+                    return ListView.builder(
+                      itemBuilder: (BuildContext context, int index) {
+                        return _Produk(
+                            product: items[index],
+                            idSupplier: widget.idSupplier,
+                            namaSupplier: widget.namaSupplier,
+                            search: _search,
+                            );
+                      },
+                      itemCount: items.length,
+                      shrinkWrap: true,
+                    );
+                  }),
+            ),
+          )
+        ],
+    );
+  }
+
+  Widget sortModal() {
+    return StatefulBuilder(
+      builder: (context, setStateModal) {
+        return Container(
+          height: MediaQuery.of(context).size.height / 3,
+          padding: EdgeInsets.symmetric(horizontal: 10),
+          decoration: BoxDecoration(
+            color: Colors.white,
+            borderRadius: BorderRadius.only(
+              topLeft: const Radius.circular(25.0),
+              topRight: const Radius.circular(25.0),
+            ),
+          ),
+          child: Column(
+            mainAxisAlignment: MainAxisAlignment.center,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: <Widget>[
+              Row(
+                crossAxisAlignment: CrossAxisAlignment.center,
+                mainAxisAlignment: MainAxisAlignment.center,
+                mainAxisSize: MainAxisSize.min,
+                children: <Widget>[
+                  Expanded(
+                    flex: 2,
+                    child: DropdownButton<String>(
+                        value: fieldSelected,
+                        style: TextStyle(
+                            color: ColorConstants.redPrimary,
+                            fontSize: 18,
+                            fontWeight: FontWeight.w700,
+                            decorationStyle: TextDecorationStyle.solid,
+                            decorationColor: ColorConstants.bluePrimary,
+                            decorationThickness: 2),
+                        icon: Icon(Icons.arrow_downward,
+                            color: ColorConstants.bluePrimary),
+                        underline: Container(
+                          height: 2,
+                          color: ColorConstants.bluePrimary,
+                        ),
+                        items: field
+                            .map<DropdownMenuItem<String>>(
+                                (String e) => DropdownMenuItem<String>(
+                                      child: Text(
+                                        e.toUpperCase(),
+                                      ),
+                                      value: e,
+                                    ))
+                            .toList(),
+                        hint: Text(
+                          'Sort by',
+                          style: TextStyle(color: ColorConstants.bluePrimary),
+                        ),
+                        iconSize: 24,
+                        onChanged: (val) {
+                          setStateModal(() {
+                            fieldSelected = val;
+                          });
+                          setState(() {
+                            fieldSelected = val;
+                          });
+                        }),
+                  ),
+                  Expanded(
+                    flex: 2,
+                    child: Column(
+                      crossAxisAlignment: CrossAxisAlignment.center,
+                      mainAxisAlignment: MainAxisAlignment.center,
+                      children: <Widget>[
+                        RadioListTile<bool>(
+                          title: const Text('Ascending'),
+                          value: false,
+                          groupValue: descending,
+                          onChanged: (bool value) {
+                            setStateModal(() {
+                              descending = value;
+                              fieldSelected = fieldSelected ?? field[0];
+                            });
+                            setState(() {
+                              fieldSelected = fieldSelected ?? field[0];
+                              descending = value;
+                            });
+                          },
+                        ),
+                        RadioListTile<bool>(
+                          title: const Text('Descending'),
+                          value: true,
+                          groupValue: descending,
+                          onChanged: (bool value) {
+                            setStateModal(() {
+                              descending = value;
+                              fieldSelected = fieldSelected ?? field[0];
+                            });
+                            setState(() {
+                              fieldSelected = fieldSelected ?? field[0];
+                              descending = value;
+                            });
+                          },
+                        ),
+                      ],
+                    ),
+                  )
+                ],
+              ),
+              SizedBox(
+                  width: double.infinity,
+                  child: RaisedButton(
+                    onPressed: () {
+                      setStateModal(() {
+                        fieldSelected = null;
+                      });
+                      setState(() {
+                        fieldSelected = null;
+                      });
+                    },
+                    child: Text('Clear'),
+                    color: ColorConstants.redPrimary,
+                    textColor: Colors.white,
+                  ))
+            ],
+          ),
+        );
+      },
+    );
+  }
+}
+
+class _Produk extends StatelessWidget {
+  final ProductSupplier product;
+  final String idSupplier;
+  final String namaSupplier;
+  final String search;
+
+  const _Produk(
+      {Key key,
+      @required this.product,
+      @required this.idSupplier,
+      @required this.namaSupplier,
+      this.search})
+      : assert(product != null),
+        super(key: key);
+  @override
+  Widget build(BuildContext context) {
+    final itemRepo = ItemRepository();
+    return Container(
+      child: StreamBuilder<DocumentSnapshot>(
+          stream:
+              itemRepo.itemDocumentFromPath(path: product.idItem).snapshots(),
+          builder: (context, snapshot) {
+            if (snapshot.hasError) {
+              SchedulerBinding.instance.addPostFrameCallback((_) {
+                Scaffold.of(context).showErrorSnackBar(
+                    'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+              });
+            }
+            if (snapshot.connectionState == ConnectionState.waiting) {
+              return Center(child: CircularProgressIndicator());
+            }
+            final item = itemRepo.item(snapshot);
+            var searchTarget = (item.nama ?? '') + (item.deskripsi ?? '') + (item.manfaat ?? '');
+            var shouldShow = searchTarget.toLowerCase().contains(search.toLowerCase());
+            return GestureDetector(
+              onTap: () {
+                Navigator.of(context).pushNamed(AppRoute.detailProductSupplier(
+                    idSupplier: idSupplier, idBarang: product.id));
+              },
+              child: shouldShow ? SizedBox(
+                height: MediaQuery.of(context).size.height / 7,
+                child:
+                CardItemSupplier(
+                harga: product.harga,
+                itemName: item.nama,
+                stock: product.stok,
+                supplierName: namaSupplier,
+                itemImage: item.url,
+              )) : Container(),
+            );
+          }),
+    );
+  }
+}
diff --git a/lib/src/screens/supplier/list_supplier.dart b/lib/src/screens/supplier/list_supplier.dart
new file mode 100644
index 0000000000000000000000000000000000000000..4cd609c931938a6cb6aecb5a7e7377def413863f
--- /dev/null
+++ b/lib/src/screens/supplier/list_supplier.dart
@@ -0,0 +1,79 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+
+// Extension import
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+import 'package:mobile_apps/src/repositories/supplier_repository.dart';
+import 'package:mobile_apps/src/screens/supplier/supplier.dart';
+
+import '../../routes.dart';
+
+class ListSupplierWidget extends StatefulWidget {
+  @override
+  _ListSupplierWidgetState createState() => _ListSupplierWidgetState();
+}
+
+class _ListSupplierWidgetState extends State<ListSupplierWidget> {
+  List semuaSupplier = [];
+  @override
+  Widget build(BuildContext context) {
+    final repository = SupplierRepository();
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: <Widget>[
+        Row(
+          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+          children: <Widget>[
+            Padding(
+                padding: EdgeInsets.only(bottom: 12.0),
+                child: Text(
+                  'Supplier',
+                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
+                )),
+            Padding(
+                padding: EdgeInsets.only(bottom: 12.0),
+                child: GestureDetector(
+                  onTap: () {
+                    Navigator.of(context).pushNamed(AppRoute.allSuppliers);
+                  },
+                  child: Text(
+                    'Lihat Semua',
+                    style: TextStyle(color: ColorConstants.redPrimary),
+                  ),
+                )),
+          ],
+        ),
+        StreamBuilder(
+          stream: repository.supplierCollections.snapshots(),
+          builder: (_, snapshot) {
+            if (snapshot.hasError) {
+              SchedulerBinding.instance.addPostFrameCallback((_) {
+                Scaffold.of(context).showErrorSnackBar(
+                    'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+              });
+            }
+            if (snapshot.connectionState == ConnectionState.waiting) {
+              return Center(child: CircularProgressIndicator());
+            }
+            semuaSupplier = repository.semuaSupplier(snapshot).take(5).toList();
+            return Container(
+              height: MediaQuery.of(context).size.height * 0.265,
+              child: ListView.builder(
+                itemBuilder: (BuildContext context, int index) {
+                  return SupplierWidget(
+                    supplier: semuaSupplier[index],
+                  );
+                },
+                itemCount: semuaSupplier.length,
+                shrinkWrap: true,
+                scrollDirection: Axis.horizontal,
+              ),
+            );
+          },
+        ),
+      ],
+    );
+  }
+}
diff --git a/lib/src/screens/supplier/product/detailproduct.dart b/lib/src/screens/supplier/product/detailproduct.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d418cbd29b11f18ab79728c7866ebb0ffaec98b4
--- /dev/null
+++ b/lib/src/screens/supplier/product/detailproduct.dart
@@ -0,0 +1,478 @@
+import 'package:cached_network_image/cached_network_image.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:intl/intl.dart';
+import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/appbar/sigapappbar.dart';
+import 'package:mobile_apps/src/models/product_supplier.dart';
+import 'package:mobile_apps/src/repositories/item_repository.dart';
+import 'package:mobile_apps/src/repositories/product_supplier_repository.dart';
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+import 'package:mobile_apps/src/repositories/supplier_repository.dart';
+import 'package:mobile_apps/src/routes.dart';
+import 'package:mobile_apps/src/screens/home/kontak/kontak.dart';
+import 'package:mobile_apps/src/common/functions.dart';
+
+class DetailProductSupplier extends StatefulWidget {
+  final String idSupplier;
+  final String idBarang;
+  DetailProductSupplier({@required this.idSupplier, @required this.idBarang});
+  @override
+  _DetailProductSupplierState createState() => _DetailProductSupplierState();
+}
+
+class _DetailProductSupplierState extends State<DetailProductSupplier> {
+  int jumlahPembelian = 0;
+  ProductSupplier product;
+  bool berhasilPesan = false;
+  @override
+  Widget build(BuildContext context) {
+    final repository = ProductSupplierRepository();
+    return Scaffold(
+      appBar: SigapAppBar(
+        title: 'Detail Produk',
+      ),
+      body: StreamBuilder(
+          stream: repository
+              .oneProductFromSupplier(
+                  idSupplier: widget.idSupplier, idProduct: widget.idBarang)
+              .snapshots(),
+          builder: (context, snapshot) {
+            if (snapshot.hasError) {
+              SchedulerBinding.instance.addPostFrameCallback((_) {
+                Scaffold.of(context).showErrorSnackBar(
+                    'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+              });
+            }
+            ;
+            if (snapshot.connectionState == ConnectionState.waiting) {
+              return Center(child: CircularProgressIndicator());
+            }
+            ;
+            product = repository.product(snapshot);
+            final itemRepository = ItemRepository();
+            final supplierRepository = SupplierRepository();
+            return StreamBuilder(
+                stream: itemRepository
+                    .itemDocumentFromPath(path: product.idItem)
+                    .snapshots(),
+                builder: (context, itemSnapshot) {
+                  if (itemSnapshot.hasError) {
+                    SchedulerBinding.instance.addPostFrameCallback((_) {
+                      Scaffold.of(context).showErrorSnackBar(
+                          'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                    });
+                  }
+                  ;
+                  if (itemSnapshot.connectionState == ConnectionState.waiting) {
+                    return Center(child: CircularProgressIndicator());
+                  }
+                  ;
+                  final item = itemRepository.item(itemSnapshot);
+                  return ListView(
+                    children: <Widget>[
+                      Container(
+                        margin: EdgeInsets.only(top: 33, left: 19, bottom: 5),
+                        child: Column(
+                          crossAxisAlignment: CrossAxisAlignment.start,
+                          children: <Widget>[
+                            Text(
+                              item.nama,
+                              style: TextStyle(
+                                  fontSize: 20, fontWeight: FontWeight.bold),
+                              overflow: TextOverflow.ellipsis,
+                            ),
+                            Text(
+                              'Dientry ${DateFormat('dd MMMM yyy hh:mm').format(product.timestamp)} oleh ${product.admin}',
+                              style: TextStyle(
+                                  color: ColorConstants.muteText, fontSize: 12),
+                            ),
+                          ],
+                        ),
+                      ),
+                      SizedBox(
+                        height: 194,
+                        child: kIsWeb
+                            ? Container(
+                                decoration: BoxDecoration(
+                                    image: DecorationImage(
+                                        image: NetworkImage(item.url),
+                                        fit: BoxFit.scaleDown)),
+                              )
+                            : CachedNetworkImage(
+                                imageUrl: item.url,
+                                imageBuilder: (context, imageProvider) =>
+                                    Container(
+                                  decoration: BoxDecoration(
+                                      image: DecorationImage(
+                                          image: imageProvider,
+                                          fit: BoxFit.scaleDown)),
+                                ),
+                                placeholder: (context, _) => const Center(
+                                    child: CircularProgressIndicator()),
+                                errorWidget: (context, url, error) => Container(
+                                  decoration: BoxDecoration(
+                                      image: DecorationImage(
+                                          image: AssetImage(
+                                              'assets/images/medical-item-placeholder.png'),
+                                          fit: BoxFit.scaleDown)),
+                                ),
+                              ),
+                      ),
+                      Padding(
+                        padding: EdgeInsets.symmetric(
+                            vertical: MarginConstants.verticalFromScreen,
+                            horizontal: MarginConstants.horizontalFromScreen),
+                        child: Card(
+                          child: Container(
+                            margin: EdgeInsets.symmetric(
+                                vertical: 23,
+                                horizontal:
+                                    MarginConstants.horizontalFromScreen),
+                            child: Column(
+                              mainAxisAlignment: MainAxisAlignment.spaceAround,
+                              crossAxisAlignment: CrossAxisAlignment.start,
+                              children: <Widget>[
+                                Text(
+                                  "${NumberFormat.simpleCurrency(locale: 'id').format(product.harga)}/pcs",
+                                  style: TextStyle(
+                                      color: ColorConstants.redSecondary,
+                                      fontWeight: FontWeight.bold,
+                                      fontSize: 20),
+                                ),
+                                _description('Stok', '${product.stok} buah'),
+                                _description(
+                                    'Spesifikasi', '${product.spesifikasi}'),
+                                _description('Manfaat', '${item.manfaat}')
+                              ],
+                            ),
+                          ),
+                        ),
+                      ),
+                      Padding(
+                          padding: EdgeInsets.only(
+                              bottom: MarginConstants.verticalFromScreen),
+                          child: Card(
+                            child: Container(
+                              margin: EdgeInsets.symmetric(
+                                  vertical: 26, horizontal: 32),
+                              child: StreamBuilder(
+                                  stream: supplierRepository
+                                      .supplierDocument(
+                                          idSupplier: widget.idSupplier)
+                                      .snapshots(),
+                                  builder: (context, supplierSnapshot) {
+                                    if (supplierSnapshot.hasError) {
+                                      SchedulerBinding.instance
+                                          .addPostFrameCallback((_) {
+                                        Scaffold.of(context).showErrorSnackBar(
+                                            'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                                      });
+                                    }
+                                    ;
+                                    if (supplierSnapshot.connectionState ==
+                                        ConnectionState.waiting) {
+                                      return Center(
+                                          child: CircularProgressIndicator());
+                                    }
+                                    ;
+                                    final supplier = supplierRepository
+                                        .supplier(supplierSnapshot);
+                                    return Column(
+                                      crossAxisAlignment:
+                                          CrossAxisAlignment.start,
+                                      children: <Widget>[
+                                        Text(
+                                          supplier.nama,
+                                          style: TextStyle(
+                                            fontSize: 20,
+                                            fontWeight: FontWeight.bold,
+                                          ),
+                                          overflow: TextOverflow.ellipsis,
+                                        ),
+                                        Container(
+                                          margin:
+                                              EdgeInsets.symmetric(vertical: 4),
+                                          child: Column(
+                                            mainAxisAlignment:
+                                                MainAxisAlignment.center,
+                                            crossAxisAlignment:
+                                                CrossAxisAlignment.start,
+                                            children: <Widget>[
+                                              if (supplier.telepon != null)
+                                                KontakButton(
+                                                    Icon(Icons.phone_in_talk),
+                                                    'Telepon ${supplier.telepon}',
+                                                    () => launchURL(
+                                                          supplier.telepon,
+                                                          scheme: 'tel',
+                                                        )),
+                                              if (supplier.telepon != null)
+                                                KontakButton(
+                                                    Icon(MdiIcons.whatsapp),
+                                                    'WhatsApp ${supplier.telepon}',
+                                                    () => launchURL(
+                                                          '//wa.me/${cleanPhoneNumber(supplier.telepon)}',
+                                                          scheme: 'https',
+                                                        )),
+                                            ],
+                                          ),
+                                        ),
+                                        InkWell(
+                                            child: Container(
+                                                padding: EdgeInsets.symmetric(
+                                                    vertical: 10),
+                                                decoration: BoxDecoration(
+                                                    color: Colors.transparent,
+                                                    borderRadius:
+                                                        BorderRadius.circular(
+                                                            50),
+                                                    border: Border.all(
+                                                        color: ColorConstants
+                                                            .redPrimary)),
+                                                child: Center(
+                                                  child: Text(
+                                                      'Lihat Detail Supplier',
+                                                      style: TextStyle(
+                                                          color: ColorConstants
+                                                              .redPrimary,
+                                                          fontWeight:
+                                                              FontWeight.w600,
+                                                          fontSize: 16)),
+                                                )),
+                                            onTap: () => Navigator.of(context)
+                                                .pushNamed(AppRoute.supplier(
+                                                    idSupplier:
+                                                        widget.idSupplier)))
+                                      ],
+                                    );
+                                  }),
+                            ),
+                          )),
+                      Container(
+                        width: MediaQuery.of(context).size.width,
+                        margin: EdgeInsets.only(top: 13),
+                        padding:
+                            EdgeInsets.symmetric(vertical: 16, horizontal: 20),
+                        decoration: BoxDecoration(color: Colors.white),
+                        child: InkWell(
+                            onTap: () => showModalBottomSheet(
+                                context: context,
+                                builder: (context) => sortModal(),
+                                backgroundColor: Colors.transparent,
+                                isScrollControlled: true),
+                            child: Container(
+                              decoration: BoxDecoration(
+                                color: ColorConstants.redPrimary,
+                                borderRadius: BorderRadius.circular(50),
+                              ),
+                              padding: EdgeInsets.symmetric(vertical: 10),
+                              child: Text('Tambah Ke Keranjang',
+                                  textAlign: TextAlign.center,
+                                  style: TextStyle(
+                                      fontWeight: FontWeight.bold,
+                                      color: Colors.white,
+                                      fontSize: 16)),
+                            )),
+                      )
+                    ],
+                  );
+                });
+          }),
+    );
+  }
+
+  Widget _description(String title, String description) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: <Widget>[
+        Text(
+          title,
+          style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
+        ),
+        Text(
+          description,
+          style: TextStyle(fontSize: 16),
+        )
+      ],
+    );
+  }
+
+  Widget sortModal() {
+    return StatefulBuilder(
+      builder: (context, setStateModal) {
+        return Container(
+          height: MediaQuery.of(context).size.height / 3,
+          padding: EdgeInsets.only(top: 37),
+          decoration: BoxDecoration(
+            color: Colors.white,
+            borderRadius: BorderRadius.only(
+              topLeft: const Radius.circular(25.0),
+              topRight: const Radius.circular(25.0),
+            ),
+          ),
+          child: Stack(
+            children: <Widget>[
+              Opacity(
+                  opacity: berhasilPesan ? 1 : 0,
+                  child: InkWell(
+                    onTap: () {
+                      setStateModal(() {
+                        berhasilPesan = false;
+                      });
+                    },
+                    child: Center(
+                        child: Row(
+                      children: <Widget>[
+                        Expanded(
+                            child: Icon(
+                          Icons.shopping_cart,
+                          size: 100,
+                          color: ColorConstants.redPrimary,
+                        )),
+                        Expanded(
+                            child: Text(
+                                'Produk berhasil ditambahkan ke keranjang!',
+                                style: TextStyle(fontSize: 16)))
+                      ],
+                    )),
+                  )),
+              Opacity(
+                opacity: berhasilPesan ? 0 : 1,
+                child: Column(
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: <Widget>[
+                    Container(
+                        margin: EdgeInsets.only(left: 25),
+                        child: Text('Masukkan kuantitas produk')),
+                    Container(
+                      margin:
+                          EdgeInsets.symmetric(horizontal: 25, vertical: 20),
+                      child: Row(
+                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                        children: <Widget>[
+                          Text('Jumlah',
+                              style: TextStyle(
+                                  fontWeight: FontWeight.w600, fontSize: 16)),
+                          Row(
+                            children: <Widget>[
+                              InkWell(
+                                onTap: () {
+                                  setStateModal(() {
+                                    jumlahPembelian = jumlahPembelian > 0
+                                        ? jumlahPembelian - 1
+                                        : jumlahPembelian;
+                                  });
+                                },
+                                child: Container(
+                                  height: 30,
+                                  width: 30,
+                                  margin: EdgeInsets.only(right: 8),
+                                  decoration: BoxDecoration(
+                                    color: ColorConstants.redPrimary,
+                                    borderRadius: BorderRadius.circular(5),
+                                  ),
+                                  child: Center(
+                                      child: Text(
+                                    '-',
+                                    style: TextStyle(
+                                        color: Colors.white,
+                                        fontWeight: FontWeight.bold),
+                                  )),
+                                ),
+                              ),
+                              Container(
+                                  margin: EdgeInsets.only(right: 8),
+                                  child: Text(
+                                    '${jumlahPembelian}',
+                                    style:
+                                        TextStyle(fontWeight: FontWeight.bold),
+                                  )),
+                              InkWell(
+                                onTap: () {
+                                  setStateModal(() {
+                                    jumlahPembelian =
+                                        jumlahPembelian < product.stok
+                                            ? jumlahPembelian + 1
+                                            : jumlahPembelian;
+                                  });
+                                },
+                                child: Container(
+                                  height: 30,
+                                  width: 30,
+                                  decoration: BoxDecoration(
+                                    color: ColorConstants.redPrimary,
+                                    borderRadius: BorderRadius.circular(5),
+                                  ),
+                                  child: Center(
+                                      child: Text('+',
+                                          style: TextStyle(
+                                              color: Colors.white,
+                                              fontWeight: FontWeight.bold))),
+                                ),
+                              ),
+                              Divider(
+                                color: ColorConstants.grayPrimary
+                                    .withOpacity(0.15),
+                                thickness: 1,
+                                height: 1,
+                              ),
+                            ],
+                          )
+                        ],
+                      ),
+                    ),
+                    Divider(
+                      color: ColorConstants.grayPrimary.withOpacity(0.15),
+                      thickness: 1,
+                      height: 1,
+                    ),
+                    Container(
+                      margin:
+                          EdgeInsets.symmetric(horizontal: 25, vertical: 20),
+                      child: Row(
+                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                        children: <Widget>[
+                          Text('Total',
+                              style: TextStyle(
+                                  fontWeight: FontWeight.w600, fontSize: 16)),
+                          Text('Rp ${jumlahPembelian * product.harga}',
+                              style: TextStyle(
+                                  fontWeight: FontWeight.w600, fontSize: 16))
+                        ],
+                      ),
+                    ),
+                    Expanded(
+                      child: InkWell(
+                        onTap: () {
+                          setStateModal(() {
+                            berhasilPesan = true;
+                          });
+                        },
+                        child: Container(
+                            padding: EdgeInsets.symmetric(vertical: 21),
+                            color: ColorConstants.redPrimary,
+                            child: Center(
+                              child: Text(
+                                'Tambah ke Keranjang',
+                                style: TextStyle(
+                                    color: Colors.white,
+                                    fontSize: 16,
+                                    fontWeight: FontWeight.bold),
+                              ),
+                            )),
+                      ),
+                    )
+                  ],
+                ),
+              ),
+            ],
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/src/screens/supplier/supplier.dart b/lib/src/screens/supplier/supplier.dart
new file mode 100644
index 0000000000000000000000000000000000000000..3a27a6b2603a70b50e8f36d7697e23faeea70e77
--- /dev/null
+++ b/lib/src/screens/supplier/supplier.dart
@@ -0,0 +1,92 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/buttons/buttons.dart';
+import 'package:mobile_apps/src/models/supplier.dart';
+import 'package:mobile_apps/src/routes.dart';
+
+class SupplierWidget extends StatelessWidget {
+  final Supplier supplier;
+
+  const SupplierWidget({Key key, @required this.supplier})
+      : assert(supplier != null),
+        super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Card(
+        child: Padding(
+      padding: EdgeInsets.only(top: 20),
+      child: FittedBox(
+        fit: BoxFit.fitHeight,
+        child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            mainAxisAlignment: MainAxisAlignment.center,
+            mainAxisSize: MainAxisSize.min,
+            children: <Widget>[
+              Container(
+                width: 380,
+                child: ListTile(
+                    title: Text(supplier.nama,
+                        style: TextStyle(
+                            fontSize: 16, fontWeight: FontWeight.w600)),
+                    subtitle: Text('${supplier.alamat}',
+                        style: TextStyle(fontSize: 12)),
+                    trailing: Icon(Icons.share)),
+              ),
+              Padding(
+                padding: const EdgeInsets.symmetric(
+                    horizontal: MarginConstants.horizontalFromScreen),
+                child: Row(
+                  mainAxisAlignment: MainAxisAlignment.start,
+                  children: <Widget>[
+                    Icon(
+                      Icons.store,
+                      color: Colors.black,
+                    ),
+                    Padding(
+                      padding: EdgeInsets.only(left: 4),
+                      child: Text('Perusahaan Terbuka'),
+                    )
+                  ],
+                ),
+              ),
+              Row(
+                mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                children: <Widget>[
+                  Padding(
+                    padding:
+                        EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
+                    child: RoundedButton(text: 'Subscribe',
+                        elevation: 0.0,
+                        padding: EdgeInsets.symmetric(horizontal: 50.0),
+                        radius: 32.0,
+                        style: TextStyle(
+                            color: Colors.white,
+                            fontSize: 12.0,
+                            fontWeight: FontWeight.bold),
+                        onPressed: () {}),
+                  ),
+                  Padding(
+                    padding:
+                        EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
+                    child: RoundedButton(text: 'Detail',
+                        elevation: 0.0,
+                        padding: EdgeInsets.symmetric(horizontal: 66.0),
+                        radius: 32.0,
+                        style: TextStyle(
+                            color: Colors.white,
+                            fontSize: 12.0,
+                            fontWeight: FontWeight.bold), onPressed: () {
+                      Navigator.of(context).pushNamed(AppRoute.supplier(
+                        idSupplier: supplier.id,
+                      ));
+                    }),
+                  ),
+                ],
+              ),
+            ]),
+      ),
+    ));
+  }
+}
diff --git a/lib/src/screens/supplier/tabs.dart b/lib/src/screens/supplier/tabs.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b78845e48b45587d8ca2bcb7c2e55e06e96c074e
--- /dev/null
+++ b/lib/src/screens/supplier/tabs.dart
@@ -0,0 +1,115 @@
+import 'package:flutter/material.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/screens/supplier/dokumen.dart';
+import 'package:mobile_apps/src/screens/supplier/ulasan.dart';
+import 'package:mobile_apps/src/screens/supplier/list_produk.dart';
+
+class SupplierTabs extends StatefulWidget {
+  final fillPercent = 65.23;
+  final String idSupplier;
+  final String namaSupplier;
+
+  SupplierTabs({@required this.idSupplier, @required this.namaSupplier});
+
+  @override
+  _SupplierTabsState createState() => _SupplierTabsState();
+}
+
+class _SupplierTabsState extends State<SupplierTabs>
+    with SingleTickerProviderStateMixin {
+  TabController _tabController;
+  @override
+  void initState() {
+    _tabController = TabController(vsync: this, length: 4);
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final fillStop = (100 - widget.fillPercent) / 100;
+    final tabTextStyle = TextStyle(
+        fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.white);
+    return SizedBox(
+        height: MediaQuery.of(context).size.height * 0.75,
+        child: Container(
+            padding: EdgeInsets.only(
+                left: MarginConstants.horizontalFromScreen + 5,
+                right: MarginConstants.horizontalFromScreen + 5,
+                top: MarginConstants.verticalFromScreen + 5),
+            decoration: BoxDecoration(
+                gradient: LinearGradient(colors: [
+              ColorConstants.redSecondary,
+              ColorConstants.redSecondary,
+              Colors.white,
+              Colors.white
+            ], stops: [
+              0.0,
+              fillStop,
+              fillStop,
+              1
+            ], end: Alignment.bottomCenter, begin: Alignment.topCenter)),
+            child: Column(children: <Widget>[
+              TabBar(
+                indicatorSize: TabBarIndicatorSize.tab,
+                indicatorColor: Colors.white,
+                isScrollable: true,
+                controller: _tabController,
+                tabs: [
+                  Container(
+                      width: MediaQuery.of(context).size.width / 4.5,
+                      child: Tab(icon: Text('Produk', style: tabTextStyle))),
+                  Container(
+                      width: MediaQuery.of(context).size.width / 4.5,
+                      child:
+                          Tab(icon: Text('Rekomendasi', style: tabTextStyle))),
+                  Container(
+                      width: MediaQuery.of(context).size.width / 4.5,
+                      child: Tab(icon: Text('Ulasan', style: tabTextStyle))),
+                  Container(
+                      width: MediaQuery.of(context).size.width / 4.5,
+                      child: Tab(icon: Text('Dokumen', style: tabTextStyle))),
+                ],
+              ),
+              Expanded(
+                  child: TabBarView(
+                controller: _tabController,
+                children: <Widget>[
+                  Container(
+                    margin: EdgeInsets.symmetric(
+                      horizontal: MarginConstants.horizontalFromScreen,
+                      vertical: MarginConstants.verticalFromScreen,
+                    ),
+                    child: ListProdukWidget(
+                        idSupplier: widget.idSupplier,
+                        namaSupplier: widget.namaSupplier),
+                  ),
+                  Container(
+                    margin: EdgeInsets.symmetric(
+                      horizontal: MarginConstants.horizontalFromScreen,
+                      vertical: MarginConstants.verticalFromScreen,
+                    ),
+                    child: ListRekomendasiWidget(widget.idSupplier),
+                  ),
+                  Container(
+                      margin: EdgeInsets.symmetric(
+                        horizontal: MarginConstants.horizontalFromScreen,
+                        vertical: MarginConstants.verticalFromScreen,
+                      ),
+                      child: ListUlasanWidget(widget.idSupplier)),
+                  Container(
+                      margin: EdgeInsets.symmetric(
+                        horizontal: MarginConstants.horizontalFromScreen,
+                        vertical: MarginConstants.verticalFromScreen,
+                      ),
+                      child: ListDokumenWidget(widget.idSupplier)),
+                ],
+              ))
+            ])));
+  }
+
+  @override
+  void dispose() {
+    _tabController.dispose();
+    super.dispose();
+  }
+}
diff --git a/lib/src/screens/supplier/ulasan.dart b/lib/src/screens/supplier/ulasan.dart
new file mode 100644
index 0000000000000000000000000000000000000000..413a2d8e99252b1e2429795c527e09641ed50753
--- /dev/null
+++ b/lib/src/screens/supplier/ulasan.dart
@@ -0,0 +1,190 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:flutter/widgets.dart';
+import 'package:mobile_apps/src/common/constants.dart';
+import 'package:mobile_apps/src/components/buttons/buttons.dart';
+import 'package:mobile_apps/src/components/cards/cards.dart';
+import 'package:mobile_apps/src/extensions/scaffold_extension.dart';
+import 'package:mobile_apps/src/repositories/ulasan_repositories.dart';
+import 'package:mobile_apps/src/screens/supplier/ulasan/form_ulasan.dart';
+
+class ListUlasanWidget extends StatelessWidget {
+  final String idSupplier;
+  final String title;
+
+  const ListUlasanWidget(this.idSupplier, {this.title = 'Ulasan Pembeli'});
+  @override
+  Widget build(BuildContext context) {
+    return _BaseListUlasanWidget(idSupplier, title,
+        collection: 'ulasan',
+        button: Column(
+            mainAxisAlignment: MainAxisAlignment.center,
+            mainAxisSize: MainAxisSize.min,
+            children: <Widget>[
+              Padding(
+                  padding: EdgeInsets.only(top: 20.0, bottom: 12.0),
+                  child: Text('Sudah pernah membeli produk dari supplier ini?',
+                      style: Theme.of(context).textTheme.subtitle2)),
+              RoundedButton(
+                padding: EdgeInsets.symmetric(horizontal: 40.0, vertical: 8.0),
+                child: Row(
+                    mainAxisAlignment: MainAxisAlignment.center,
+                    mainAxisSize: MainAxisSize.min,
+                    children: <Widget>[
+                      Padding(
+                          padding: EdgeInsets.only(right: 12.0),
+                          child: Icon(
+                            Icons.add_circle_outline,
+                            color: Colors.white,
+                          )),
+                      Text(
+                        'Buat Ulasan Baru',
+                        style: Theme.of(context)
+                            .textTheme
+                            .headline6
+                            .copyWith(color: Colors.white),
+                      )
+                    ]),
+                color: ColorConstants.redSecondary,
+                radius: 32.0,
+                onPressed: () {
+                  Navigator.push(
+                    context,
+                    MaterialPageRoute(
+                        builder: (context) => UlasanInput(
+                              id: idSupplier,
+                            )),
+                  );
+                },
+              ),
+            ]));
+  }
+}
+
+class ListRekomendasiWidget extends StatelessWidget {
+  final String idSupplier;
+  final String title;
+  final Color leftDecorationColor;
+
+  const ListRekomendasiWidget(this.idSupplier,
+      {this.title = 'Direkomendasikan oleh',
+      this.leftDecorationColor = ColorConstants.muteText});
+
+  @override
+  Widget build(BuildContext context) {
+    return _BaseListUlasanWidget(idSupplier, title,
+        collection: 'rekomendasi', leftDecorationColor: leftDecorationColor);
+  }
+}
+
+class _BaseListUlasanWidget extends StatefulWidget {
+  final String idSupplier;
+  final String collection;
+  final String title;
+  final Color leftDecorationColor;
+  final Widget button;
+
+  const _BaseListUlasanWidget(this.idSupplier, this.title,
+      {@required this.collection, this.leftDecorationColor, this.button});
+
+  @override
+  __BaseListUlasanWidgetState createState() => __BaseListUlasanWidgetState();
+}
+
+class __BaseListUlasanWidgetState extends State<_BaseListUlasanWidget> {
+  bool descending;
+  UlasanRepository ulasanRepo;
+  Query ulasanCollections;
+
+  @override
+  void initState() {
+    ulasanRepo = UlasanRepository();
+    ulasanCollections = ulasanRepo.ulasanSupplierCollections(
+        widget.idSupplier, widget.collection);
+    descending = true;
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    ulasanCollections = ulasanRepo.ulasanSupplierCollections(
+        widget.idSupplier, widget.collection);
+    ulasanCollections =
+        ulasanCollections.orderBy('timestamp', descending: descending);
+    return Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: <Widget>[
+          if (widget.title != null)
+            Padding(
+                padding: EdgeInsets.only(bottom: 12.0),
+                child: Text(
+                  widget.title,
+                  style: Theme.of(context)
+                      .textTheme
+                      .headline5
+                      .copyWith(color: Colors.white),
+                )),
+          Flexible(
+              child: Column(
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: <Widget>[
+              Flexible(
+                  child: StreamBuilder(
+                stream: ulasanCollections.snapshots(),
+                builder: (_, snapshot) {
+                  if (snapshot.hasError) {
+                    SchedulerBinding.instance.addPostFrameCallback((_) {
+                      Scaffold.of(context).showErrorSnackBar(
+                          'Oops! Ada sesuatu yang salah, mohon coba lagi ya.');
+                    });
+                  }
+                  if (snapshot.connectionState == ConnectionState.waiting) {
+                    return Center(child: CircularProgressIndicator());
+                  }
+                  var allUlasan = ulasanRepo.allUlasan(snapshot);
+                  if (allUlasan.isEmpty) {
+                    return Padding(
+                        padding: EdgeInsets.only(bottom: 64.0),
+                        child: Card(
+                            child: Container(
+                                width: double.infinity,
+                                padding: EdgeInsets.all(12.0),
+                                child: Text(
+                                    "Belum ada ${widget.collection == 'rekomendasi' ? 'rekomendasi' : 'ulasan'}.",
+                                    style: Theme.of(context)
+                                        .textTheme
+                                        .subtitle1))));
+                  }
+                  return ListView.builder(
+                    itemBuilder: ((context, index) {
+                      var ulasan = allUlasan[index];
+                      Widget card = GenericCard(
+                        head:
+                            "${ulasan.displayDate}${widget.collection == 'rekomendasi' ? ' oleh Admin' : ''}",
+                        title: ulasan.nama,
+                        subtitle: ulasan.jabatan,
+                        body: ulasan.body,
+                      );
+                      if (widget.leftDecorationColor != null) {
+                        card = StackOfCards(
+                          num: 2,
+                          child: card,
+                          color: widget.leftDecorationColor,
+                          offset: 15.0,
+                        );
+                      }
+                      return card;
+                    }),
+                    itemCount: allUlasan.length,
+                    shrinkWrap: true,
+                    scrollDirection: Axis.vertical,
+                  );
+                },
+              )),
+              if (widget.button != null) widget.button,
+            ],
+          ))
+        ]);
+  }
+}
diff --git a/lib/src/screens/supplier/ulasan/form_ulasan.dart b/lib/src/screens/supplier/ulasan/form_ulasan.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a36ac519eb06655784cbb8fa1bb9c4b40d97574a
--- /dev/null
+++ b/lib/src/screens/supplier/ulasan/form_ulasan.dart
@@ -0,0 +1,116 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/material.dart';
+import 'package:mobile_apps/src/components/appbar/sigapappbar.dart';
+import 'package:mobile_apps/src/components/buttons/buttons.dart';
+import 'package:mobile_apps/src/components/textform/textform.dart';
+import 'package:mobile_apps/src/services/crud_method.dart';
+
+class UlasanInput extends StatefulWidget {
+  final String id;
+
+  const UlasanInput({Key key, this.id}) : super(key: key);
+
+  @override
+  _UlasanInputState createState() => _UlasanInputState();
+}
+
+class _UlasanInputState extends State<UlasanInput> {
+  String nama;
+  String noTelpPribadi;
+  String noTelpKantor;
+  String email;
+  String jabatan;
+  String institusi;
+  String ulasan;
+  final _formKey = GlobalKey<FormState>();
+  CrudMedthods crudObj = CrudMedthods();
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: SigapAppBar(title: 'Buat Ulasan Baru'),
+      resizeToAvoidBottomInset: true,
+      resizeToAvoidBottomPadding: false,
+      body: SingleChildScrollView(
+        child: Container(
+          padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 30.0),
+          child: Form(
+            key: _formKey,
+            child: Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: <Widget>[
+                TextFormInput(
+                  field: 'Nama Lengkap',
+                  onChange: (val) => {setState(() => nama = val)},
+                  type: TextInputType.text,
+                ),
+                TextFormInput(
+                  field: 'Nomor Telepon Pribadi',
+                  onChange: (val) => {setState(() => noTelpPribadi = val)},
+                  type: TextInputType.number,
+                ),
+                TextFormInput(
+                  field: 'Nomor Telepon Kantor',
+                  onChange: (val) => {setState(() => noTelpKantor = val)},
+                  type: TextInputType.number,
+                ),
+                TextFormInput(
+                  field: 'E-mail',
+                  onChange: (val) => {setState(() => email = val)},
+                  type: TextInputType.emailAddress,
+                ),
+                TextFormInput(
+                  field: 'Jabatan',
+                  onChange: (val) => {setState(() => jabatan = val)},
+                  type: TextInputType.text,
+                ),
+                TextFormInput(
+                  field: 'Institusi',
+                  onChange: (val) => {setState(() => institusi = val)},
+                  type: TextInputType.text,
+                ),
+                TextFormInput(
+                  field: 'Ulasan',
+                  onChange: (val) => {setState(() => ulasan = val)},
+                  type: TextInputType.multiline,
+                  line: 5,
+                ),
+                Container(
+                  padding: EdgeInsets.symmetric(vertical: 8.0),
+                  width: double.infinity,
+                  child: RoundedButton(
+                    text: 'Kirim Ulasan',
+                    style: Theme.of(context).textTheme.headline6.copyWith(color: Colors.white),
+                    onPressed: () {
+                      Navigator.of(context).pop();
+                      crudObj.addData({
+                        'body': ulasan,
+                        'email': email,
+                        'institusi': institusi,
+                        'jabatan': jabatan,
+                        'nama': nama,
+                        'telepon': noTelpPribadi,
+                        'telpKantor': noTelpKantor,
+                        'timestamp': Timestamp.now()
+                      },
+                      widget.id
+                      );
+                    },
+                    padding: EdgeInsets.symmetric(vertical: 8.0),
+                    radius: 32.0,
+                )),
+                Padding(padding: EdgeInsets.symmetric(vertical: 16.0), child:
+                  Text(
+                    'Catatan: Nomor telepon pribadi dan e-mail Anda tidak akan dipublikasikan.',
+                    style: Theme.of(context).textTheme.subtitle2
+                  )
+                )
+              ],
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+}
+
diff --git a/lib/src/services/crud_method.dart b/lib/src/services/crud_method.dart
new file mode 100644
index 0000000000000000000000000000000000000000..fd979c9e541c5f6a74b7a7fcd115445e7d02864f
--- /dev/null
+++ b/lib/src/services/crud_method.dart
@@ -0,0 +1,11 @@
+import 'dart:async';
+
+import 'package:cloud_firestore/cloud_firestore.dart';
+
+class CrudMedthods {
+  Future<void> addData(ulasan, id) async {
+    await Firestore.instance.collection('waspada/covid19/supplier/$id/ulasan').add(ulasan).catchError((e) {
+      print(e);
+    });
+  }
+}
diff --git a/lib/src/theme.dart b/lib/src/theme.dart
index 29f383cac4649615981bae398deea65218d893f2..fe6408441d357226bf152bbd3641b17479d4a99a 100644
--- a/lib/src/theme.dart
+++ b/lib/src/theme.dart
@@ -1,32 +1,44 @@
 import 'package:flutter/material.dart';
 import 'package:google_fonts/google_fonts.dart';
+import 'package:mobile_apps/src/common/constants.dart';
 
 class AppColors {
   static const Color indigo = Color(0xFF096B91);
   static const Color pink = Color(0xFFEDD7D7);
   static const Color bluePrimary = Color(0xFF007AFF);
   static const Color blueAlternative = Color(0xFF82C5E6);
+
+  static const Color redPrimary = Color(0xFFD62C2C);
+  static const Color redSecondary = Color(0xFFE16E6E);
+  static const Color redTertiary = Color(0xFFEDD7D7);
 }
 
 class AppTheme {
-  static final TextTheme textTheme = GoogleFonts.poppinsTextTheme().apply(
+  static final TextTheme baseTextTheme = GoogleFonts.poppinsTextTheme().apply(
     bodyColor: Colors.black,
     displayColor: Colors.black,
   );
 
+  static final TextTheme textTheme = baseTextTheme.copyWith(
+    subtitle2:
+        baseTextTheme.subtitle2.copyWith(color: ColorConstants.grayPrimary),
+    headline5: baseTextTheme.headline5.copyWith(fontWeight: FontWeight.bold),
+    headline6: baseTextTheme.headline6.copyWith(fontWeight: FontWeight.bold),
+  );
+
   static ThemeData _customThemeData(ThemeData base) {
     final colorScheme = ColorScheme.light().copyWith(
-      primary: AppColors.bluePrimary,
-      primaryVariant: AppColors.blueAlternative,
-      secondary: AppColors.pink,
+      primary: AppColors.redPrimary,
+      primaryVariant: AppColors.redSecondary,
     );
     return base.copyWith(
-      primaryColor: AppColors.bluePrimary,
-      primaryColorLight: AppColors.blueAlternative,
-      primaryColorDark: AppColors.bluePrimary,
+      // Somehow primaryColor is used on icon background
+      primaryColor: Colors.white,
+      primaryColorLight: AppColors.redSecondary,
+      primaryColorDark: AppColors.redPrimary,
       primaryTextTheme: textTheme,
       textTheme: textTheme,
-      accentColor: AppColors.pink,
+      accentColor: AppColors.redTertiary,
       colorScheme: colorScheme,
       appBarTheme: AppBarTheme().copyWith(
         color: Colors.white,
diff --git a/lib/src/widgets/auth_guard_widget.dart b/lib/src/widgets/auth_guard_widget.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a331e12b75f7378be082b137565c540579c31c00
--- /dev/null
+++ b/lib/src/widgets/auth_guard_widget.dart
@@ -0,0 +1,22 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/scheduler.dart';
+import 'package:mobile_apps/src/services/auth_service.dart';
+import 'package:provider/provider.dart';
+
+import '../routes.dart';
+
+class AuthGuardWidget extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    final authService = Provider.of<AuthService>(context);
+    if (!authService.isAuthenticated) {
+      SchedulerBinding.instance.addPostFrameCallback((_) {
+        Navigator.of(context).pushReplacementNamed(AppRoute.login);
+      });
+      return Container();
+    }
+    return Container(
+      
+    );
+  }
+}
diff --git a/pubspec.lock b/pubspec.lock
index 6a66e8aef2887901f44e9206b8dc986a0b19627b..860eebfa42437c31a8fbdb1df03fa3f2a3715b13 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -36,6 +36,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.4.0"
+  bloc:
+    dependency: transitive
+    description:
+      name: bloc
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.0.0"
   boolean_selector:
     dependency: transitive
     description:
@@ -99,6 +106,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "7.0.9"
+  cached_network_image:
+    dependency: "direct main"
+    description:
+      name: cached_network_image
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.0.0"
   characters:
     dependency: transitive
     description:
@@ -155,6 +169,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.14.11"
+  color:
+    dependency: transitive
+    description:
+      name: color
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.1.1"
   convert:
     dependency: transitive
     description:
@@ -211,6 +232,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.1.1"
+  esys_flutter_share:
+    dependency: "direct main"
+    description:
+      name: esys_flutter_share
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.2"
   firebase:
     dependency: transitive
     description:
@@ -267,11 +295,46 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.10.11"
+  fluro:
+    dependency: "direct main"
+    description:
+      name: fluro
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.6.1"
   flutter:
     dependency: "direct main"
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_bloc:
+    dependency: "direct main"
+    description:
+      name: flutter_bloc
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.2.0"
+  flutter_cache_manager:
+    dependency: transitive
+    description:
+      name: flutter_cache_manager
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.1.3"
+  flutter_launcher_icons:
+    dependency: "direct dev"
+    description:
+      name: flutter_launcher_icons
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.7.4"
+  flutter_native_splash:
+    dependency: "direct dev"
+    description:
+      name: flutter_native_splash
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.1.9"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -359,6 +422,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.1.4"
+  intl:
+    dependency: "direct main"
+    description:
+      name: intl
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.16.1"
   io:
     dependency: transitive
     description:
@@ -387,6 +457,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "3.2.5"
+  localstorage:
+    dependency: "direct main"
+    description:
+      name: localstorage
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.0.1+4"
   logging:
     dependency: transitive
     description:
@@ -401,6 +478,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.12.6"
+  material_design_icons_flutter:
+    dependency: "direct main"
+    description:
+      name: material_design_icons_flutter
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "4.0.5045"
   meta:
     dependency: transitive
     description:
@@ -541,6 +625,20 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.0.5"
+  rxdart:
+    dependency: transitive
+    description:
+      name: rxdart
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.23.1"
+  screenshot:
+    dependency: "direct main"
+    description:
+      name: screenshot
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.1.1"
   shelf:
     dependency: transitive
     description:
@@ -574,6 +672,20 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.5.5"
+  sqflite:
+    dependency: "direct main"
+    description:
+      name: sqflite
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.3.0"
+  sqflite_common:
+    dependency: transitive
+    description:
+      name: sqflite_common
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.0+1"
   stack_trace:
     dependency: transitive
     description:
@@ -602,6 +714,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.0.5"
+  synchronized:
+    dependency: transitive
+    description:
+      name: synchronized
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.2.0"
   term_glyph:
     dependency: transitive
     description:
@@ -651,6 +770,55 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.1.6"
+  universal_html:
+    dependency: "direct main"
+    description:
+      name: universal_html
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.1.20"
+  universal_io:
+    dependency: transitive
+    description:
+      name: universal_io
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.1"
+  url_launcher:
+    dependency: "direct main"
+    description:
+      name: url_launcher
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "5.4.2"
+  url_launcher_macos:
+    dependency: transitive
+    description:
+      name: url_launcher_macos
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.0.1+4"
+  url_launcher_platform_interface:
+    dependency: transitive
+    description:
+      name: url_launcher_platform_interface
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.6"
+  url_launcher_web:
+    dependency: transitive
+    description:
+      name: url_launcher_web
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.1.1+1"
+  uuid:
+    dependency: transitive
+    description:
+      name: uuid
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.0.4"
   vector_math:
     dependency: transitive
     description:
@@ -686,6 +854,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.2.0"
+  zone_local:
+    dependency: transitive
+    description:
+      name: zone_local
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.1.2"
 sdks:
   dart: ">=2.7.0 <3.0.0"
-  flutter: ">=1.12.13+hotfix.4 <2.0.0"
+  flutter: ">=1.12.13+hotfix.4 <1.16.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 19f121ebd8c6e8072305b79acc4bcaf3679da6fa..2be2573bbcdd3ec73844f3609dafb9be054a2e7b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -19,16 +19,24 @@ environment:
 dependencies:
   flutter:
     sdk: flutter
-  # Firebase and Google Sign In Dependencies  
+  # Firebase and Google Sign In Dependencies
   firebase_core: ^0.4.0+9
   firebase_auth: ^0.15.5+3
   cloud_firestore: ^0.13.3
   google_sign_in: ^4.2.0
+  fluro: ^1.6.1
+
 
-  
   # Other Flutter dependencies
   google_fonts: ^0.3.10
   provider: ^4.0.4
+  esys_flutter_share: ^1.0.2
+  screenshot: ^0.1.1
+  cached_network_image: ^2.0.0
+  intl: ^0.16.1
+  url_launcher: ^5.4.0
+  universal_html: ^1.1.20
+  material_design_icons_flutter: 4.0.5045
 
   # Dart utils
   equatable: ^1.1.1
@@ -48,7 +56,9 @@ dev_dependencies:
   build_runner: ^1.0.0
   json_serializable: ^3.2.0
   to_string_generator: ^1.2.1
-  
+  flutter_launcher_icons: ^0.7.3
+  flutter_native_splash: ^0.1.9
+
   # Linter dependency
   pedantic: ^1.8.0 # The default Linter package used in Google
 
@@ -64,7 +74,7 @@ flutter:
   uses-material-design: true
 
   assets:
-    - assets/images/google_logo.png
+    - assets/images/
 
   # An image asset can refer to one or more resolution-specific "variants", see
   # https://flutter.dev/assets-and-images/#resolution-aware.
@@ -91,3 +101,16 @@ flutter:
   #
   # For details regarding fonts from package dependencies,
   # see https://flutter.dev/custom-fonts/#from-packages
+
+flutter_icons:
+  # Configuration for flutter_launcher_icons
+  # See https://pub.dev/packages/flutter_launcher_icons
+  android: "launcher_icon"
+  ios: true
+  image_path: "assets/icons/icon-merah.png"
+
+flutter_native_splash:
+  # See https://pub.dev/packages/flutter_native_splash
+  image: assets/images/sigap.png
+  color: fefefe
+  android_disable_fullscreen: true
diff --git a/scripts/.gitignore b/scripts/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..31607887faebf38d46354a6c13b193e7c6b543ee
--- /dev/null
+++ b/scripts/.gitignore
@@ -0,0 +1,3 @@
+.vscode/
+venv/
+*.json
diff --git a/scripts/backup.js b/scripts/backup.js
new file mode 100644
index 0000000000000000000000000000000000000000..b87bfbec3f8abbd46fd87231fc2d0000c2cdd729
--- /dev/null
+++ b/scripts/backup.js
@@ -0,0 +1,21 @@
+const firestoreService = require('firestore-export-import');
+const serviceAccount = require('./waspadabencana-staging.json');
+const fs = require('fs');
+const arrCol = ['waspada/covid19/agregasi', 'waspada/covid19/agregasi-ketersediaan', 'waspada/covid19/home-industry',
+  	'waspada/covid19/institusi', 'waspada/covid19/items', 'waspada/covid19/kontak', 'waspada/covid19/notifikasi', 'waspada/covid19/suppliers']
+// Initiate Firebase App
+firestoreService.initializeApp(serviceAccount, "https://waspadabencana-staging.firebaseio.com");
+
+// Start exporting your data
+let path = '/waspada/covid19/agregrasi-ketersediaan';
+firestoreService
+  .backup(path)
+  .then(collections => {
+  	let col = path.split('/');
+  	fs.writeFile(col[2]+".json", JSON.stringify(collections), function (err) {
+	  if (err) throw err;
+	  console.log("Saved!");
+	});
+  });
+
+
diff --git a/scripts/package.json b/scripts/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..c29d74920c10c23ab46c61879648c7bc3dd56a54
--- /dev/null
+++ b/scripts/package.json
@@ -0,0 +1,6 @@
+{
+  "dependencies": {
+    "firestore-export-import": "^0.4.0",
+    "node-firestore-import-export": "^0.14.1"
+  }
+}
diff --git a/scripts/requirements.txt b/scripts/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5800203ed404cf05090ed378b3a9e4b43beb94e6
--- /dev/null
+++ b/scripts/requirements.txt
@@ -0,0 +1,27 @@
+CacheControl==0.12.6
+cachetools==4.0.0
+certifi==2019.11.28
+chardet==3.0.4
+firebase-admin==4.0.0
+google-api-core==1.16.0
+google-api-python-client==1.8.0
+google-auth==1.12.0
+google-auth-httplib2==0.0.3
+google-cloud-core==1.3.0
+google-cloud-firestore==1.6.2
+google-cloud-storage==1.26.0
+google-resumable-media==0.5.0
+googleapis-common-protos==1.51.0
+grpcio==1.27.2
+httplib2==0.17.0
+idna==2.9
+msgpack==1.0.0
+protobuf==3.11.3
+pyasn1==0.4.8
+pyasn1-modules==0.2.8
+pytz==2019.3
+requests==2.23.0
+rsa==4.0
+six==1.14.0
+uritemplate==3.0.1
+urllib3==1.25.8
diff --git a/scripts/restore.js b/scripts/restore.js
new file mode 100644
index 0000000000000000000000000000000000000000..5847ac38f4b1079dbd91d4b44a2b95fcdbe85bb8
--- /dev/null
+++ b/scripts/restore.js
@@ -0,0 +1,11 @@
+const firestoreService = require('firestore-export-import');
+const serviceAccount = require('./waspadabencana-prod.json');
+const fs = require('fs');
+const arrCol = ['waspada/covid19/agregasi', 'waspada/covid19/agregasi-ketersediaan', 'waspada/covid19/home-industry',
+  	'waspada/covid19/institusi', 'waspada/covid19/items', 'waspada/covid19/kontak', 'waspada/covid19/notifikasi', 'waspada/covid19/suppliers']
+
+// Initiate Firebase App
+firestoreService.initializeApp(serviceAccount, "https://waspadabencana-prod.firebaseio.com");
+
+// Start importing your data
+firestoreService.restore('staging-waspada-backup-28032020.json', {});
\ No newline at end of file
diff --git a/scripts/utils.py b/scripts/utils.py
new file mode 100755
index 0000000000000000000000000000000000000000..a885cfe064e9a0b13a642ce2ffa145534aab673e
--- /dev/null
+++ b/scripts/utils.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python3
+# Please set the default credentials
+# GOOGLE_APPLICATION_CREDENTIALS=service-account.json
+import os
+import json
+import firebase_admin
+
+from firebase_admin import credentials
+from firebase_admin import firestore
+from google.cloud.firestore_v1 import (
+    CollectionReference,
+    DocumentReference,
+    DocumentSnapshot,
+)
+
+cred = credentials.ApplicationDefault()
+
+firebase_admin.initialize_app(cred)
+client = firestore.client()
+
+
+def main():
+    rename_suppliers_to_supplier()
+
+def rename_suppliers_to_supplier():
+    suppliers_collection_ref = client.collection('waspada/covid19/suppliers')
+    supplier_refs = suppliers_collection_ref.list_documents()
+
+    for supplier_ref in supplier_refs:
+        supplier_snapshot = supplier_ref.get()
+        print(supplier_ref.path)
+        print(supplier_snapshot.to_dict())
+        print([suppliers_collection_ref for supplier_subcollection_ref in supplier_ref.collections()])
+
+def copy_document_path(source_document_path: str,
+                       target_document_path: str,
+                       rename_subcollections=None,
+                       rename_fields=None):
+    source_document_ref = client.document(source_document_path)
+    target_document_ref = client.document(target_document_path)
+
+    copy_document_ref(source_document_ref, target_document_ref,
+                      rename_subcollections=rename_subcollections,
+                      rename_fields=rename_fields)
+
+def copy_document_ref(source_document_ref: DocumentReference,
+                      target_document_ref: DocumentReference,
+                      rename_subcollections=None,
+                      rename_fields=None):
+    source_document_dict = source_document_ref.get().to_dict()
+
+    if rename_fields is not None:
+        for old, new in rename_fields.items():
+            if old in source_document_dict:
+                source_document_dict[new] = source_document_dict[old]
+                del source_document_dict[old]
+
+    target_document_ref.set(source_document_dict)
+
+    source_subcollections_ref = source_document_ref.collections()
+
+    for source_subcollection_ref in source_subcollections_ref:
+        subcollection_id = source_subcollection_ref.id
+
+        if rename_subcollections is not None and subcollection_id in rename_subcollections:
+            subcollection_id = rename_subcollections[subcollection_id]
+
+        target_subcollection_ref = target_document_ref.collection(subcollection_id)
+
+        copy_collection_ref(source_subcollection_ref, target_subcollection_ref,
+                            rename_subcollections=rename_subcollections,
+                            rename_fields=rename_fields)
+
+def copy_collection_path(source_collection_path: str,
+                         target_collection_path: str,
+                         rename_subcollections=None,
+                         rename_fields=None):
+    source_collection_ref = client.collection(source_collection_path)
+    target_collection_ref = client.collection(target_collection_path)
+
+    copy_collection_ref(source_collection_ref, target_collection_ref,
+                        rename_subcollections=rename_subcollections,
+                        rename_fields=rename_fields)
+
+def copy_collection_ref(source_collection_ref: CollectionReference,
+                        target_collection_ref: CollectionReference,
+                        rename_subcollections=None,
+                        rename_fields=None):
+    source_documents_ref = source_collection_ref.list_documents()
+    for source_document_ref in source_documents_ref:
+        target_document_ref = target_collection_ref.document(source_document_ref.id)
+
+        copy_document_ref(source_document_ref, target_document_ref,
+                          rename_subcollections=rename_subcollections,
+                          rename_fields=rename_fields)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/test/navigation_test.dart b/test/navigation_test.dart
index 2aae7b9c336c6ee9fc342507c3d32d0635e76274..9a960583cb3e54955a3af19dfee52d040b53459e 100644
--- a/test/navigation_test.dart
+++ b/test/navigation_test.dart
@@ -1,10 +1,13 @@
 import 'package:flutter_test/flutter_test.dart';
 import 'package:mobile_apps/src/app.dart';
-import 'package:mobile_apps/src/screens/login/login_screen.dart';
+import 'package:mobile_apps/src/routes.dart';
+import 'package:mobile_apps/src/screens/home/home_screen.dart';
 
 void main() {
-  testWidgets('Initial route is Login', (WidgetTester tester) async {
+  AppRoute.setupRouter();
+
+  testWidgets('Initial route is Home Screen', (WidgetTester tester) async {
     await tester.pumpWidget(App());
-    expect(find.byType(LoginScreen), findsOneWidget);
+    expect(find.byType(HomeScreen), findsOneWidget);
   });
 }
diff --git a/web/index.html b/web/index.html
index be87cca17ca1722a3d4689f7ea8961e3a5847033..2591ef0a9a718e8e4633d9fae807205958352aae 100644
--- a/web/index.html
+++ b/web/index.html
@@ -17,37 +17,20 @@
     <title>COVID10HARI</title>
     <link rel="manifest" href="manifest.json" />
 
-    <meta name="google-signin-client_id" content="341221363005-ev5ijn7e8vn3qnrgjtmfjci5an4mtjij.apps.googleusercontent.com" /> 
+    <meta
+      name="google-signin-client_id"
+      content="341221363005-ev5ijn7e8vn3qnrgjtmfjci5an4mtjij.apps.googleusercontent.com"
+    />
   </head>
   <body>
     <!-- Firebase dependencies begin here-->
-    <!-- The core Firebase JS SDK is always required and must be listed first -->
-    <script src="https://www.gstatic.com/firebasejs/7.12.0/firebase-app.js"></script>
-
-    <!-- TODO: Add SDKs here if we use more Firebase products
-    https://firebase.google.com/docs/web/setup#available-libraries -->
-    <script src="https://www.gstatic.com/firebasejs/7.12.0/firebase-analytics.js"></script>
-    <script src="https://www.gstatic.com/firebasejs/7.12.0/firebase-auth.js"></script>
-    <script src="https://www.gstatic.com/firebasejs/7.12.0/firebase-firestore.js"></script>
-
-    <script>
-      // Your web app's Firebase configuration
-      var firebaseConfig = {
-        apiKey: "AIzaSyDpDw0CC0N2J-vRIS8mkUbSSyCQFZ58Htg",
-        authDomain: "corona-fasilkom.firebaseapp.com",
-        databaseURL: "https://corona-fasilkom.firebaseio.com",
-        projectId: "corona-fasilkom",
-        storageBucket: "corona-fasilkom.appspot.com",
-        messagingSenderId: "341221363005",
-        appId: "1:341221363005:web:56f1eb3c7f6c522da7c954",
-        measurementId: "G-TRYY6NYS5Z"
-      };
-      // Initialize Firebase
-      firebase.initializeApp(firebaseConfig);
-      firebase.analytics();
-    </script>
+    <script src="/__/firebase/7.12.0/firebase-app.js"></script>
+    <script src="/__/firebase/7.12.0/firebase-analytics.js"></script>
+    <script src="/__/firebase/7.12.0/firebase-auth.js"></script>
+    <script src="/__/firebase/7.12.0/firebase-firestore.js"></script>
+    <script src="/__/firebase/init.js"></script>
     <!-- Firebase dependencies end-->
-    
+
     <!-- This script installs service_worker.js to provide PWA functionality to
        application. For more information, see:
        https://developers.google.com/web/fundamentals/primers/service-workers -->