diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..8a80734f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,21 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+
+[*]
+
+# Change these settings to your own preference
+indent_style = space
+indent_size = 4
+
+# We recommend you to keep these unchanged
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 00000000..e35b92da
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,78 @@
+module.exports = {
+ root: true,
+ extends: '@react-native-community',
+ parser: 'babel-eslint',
+ plugins: ['flowtype', 'prettier'],
+ rules: {
+ 'react/jsx-filename-extension': 'off',
+ 'no-use-before-define': 'off',
+ 'import/prefer-default-export': 'off',
+ 'function-paren-newline': 'off',
+ 'no-trailing-spaces': 'off',
+ 'eol-last': 'off',
+ 'react/jsx-curly-brace-presence': 'off',
+ camelcase: 'off',
+ 'no-underscore-dangle': 'off',
+ 'object-curly-newline': 'off',
+ 'arrow-parens': 'off',
+ 'global-require': 'off',
+ 'no-else-return': 'off',
+ 'no-unused-expressions': 'off',
+ // Indent with 4 spaces
+ "indent": ["error", 4],
+ // Indent JSX with 4 spaces
+ "react/jsx-indent": ["error", 4],
+ // Indent props with 4 spaces
+ "react/jsx-indent-props": ["error", 4],
+ 'react/prefer-stateless-function': 'off',
+ 'no-confusing-arrow': 'off',
+ 'no-unused-expressions': 'off',
+ 'react/jsx-no-bind': 'off',
+ 'consistent-return': 'off',
+ "react/sort-comp": ["on", {
+ order: [
+ 'static-methods',
+ 'lifecycle',
+ 'everything-else',
+ 'render'
+ ],
+ groups: {
+ lifecycle: [
+ 'displayName',
+ 'propTypes',
+ 'contextTypes',
+ 'childContextTypes',
+ 'mixins',
+ 'statics',
+ 'defaultProps',
+ 'constructor',
+ 'getDefaultProps',
+ 'state',
+ 'getInitialState',
+ 'getChildContext',
+ 'getDerivedStateFromProps',
+ 'componentWillMount',
+ 'UNSAFE_componentWillMount',
+ 'componentDidMount',
+ 'componentWillReceiveProps',
+ 'UNSAFE_componentWillReceiveProps',
+ 'shouldComponentUpdate',
+ 'componentWillUpdate',
+ 'UNSAFE_componentWillUpdate',
+ 'getSnapshotBeforeUpdate',
+ 'componentDidUpdate',
+ 'componentDidCatch',
+ 'componentWillUnmount'
+ ]
+ }
+ }]
+ },
+ globals: {
+ fetch: true,
+ it: true,
+ alert: true,
+ btoa: true,
+ document: true,
+ window: true,
+ },
+ };
diff --git a/.expo-shared/assets.json b/.expo-shared/assets.json
new file mode 100644
index 00000000..17ad2288
--- /dev/null
+++ b/.expo-shared/assets.json
@@ -0,0 +1,4 @@
+{
+ "f9155ac790fd02fadcdeca367b02581c04a353aa6d5aa84409a59f6804c87acd": true,
+ "89ed26367cdb9b771858e026f2eb95bfdb90e5ae943e716575327ec325f39c44": true
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..48207142
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+node_modules/**/*
+.expo/*
+npm-debug.*
+*.jks
+*.p8
+*.p12
+*.key
+*.mobileprovision
+*.orig.*
+web-build/
+web-report/
diff --git a/.prettierlc.js b/.prettierlc.js
new file mode 100644
index 00000000..5500028d
--- /dev/null
+++ b/.prettierlc.js
@@ -0,0 +1,10 @@
+module.exports = {
+ bracketSpacing: true,
+ jsxBracketSameLine: false,
+ singleQuote: true,
+ trailingComma: 'all',
+ printWidth: 100,
+ parser: "flow",
+ arrowParens: "always",
+ tabWidth: 4,
+ };
diff --git a/.watchmanconfig b/.watchmanconfig
new file mode 100644
index 00000000..0967ef42
--- /dev/null
+++ b/.watchmanconfig
@@ -0,0 +1 @@
+{}
diff --git a/App.js b/App.js
new file mode 100644
index 00000000..9a744331
--- /dev/null
+++ b/App.js
@@ -0,0 +1,10 @@
+/**
+ * Main Root component imported for export crater app.
+ *
+ * @version 1.0.0
+ * @author [crater](https://craterapp.com)
+ */
+
+import App from './src/index'
+
+export default App
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..6b60e946
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,47 @@
+Copyright (c) 2019 by Mohit Panjwani
+Crater Invoice * https://www.craterapp.com
+"Easy Invoicing"
+
+All Rights Reserved
+ATTRIBUTION ASSURANCE LICENSE (adapted from the original BSD license)
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the conditions below are met.
+These conditions require a modest attribution to CraterApp.com (the
+"Author"), who hopes that its promotional value may help justify the
+thousands of dollars in otherwise billable time invested in writing
+this and other freely available, open-source software.
+
+1. Redistributions of source code, in whole or part and with or without
+modification (the "Code"), must prominently display this GPG-signed
+text in verifiable form.
+2. Redistributions of the Code in binary form must be accompanied by
+this GPG-signed text in any documentation and, each time the resulting
+executable program or a program dependent thereon is launched, a
+prominent display (e.g., splash screen or banner text) of the Author's
+attribution information, which includes:
+(a) Name ("Mohit Panjwani"),
+(b) Professional identification ("Crater Invoice"), and
+(c) URL ("https://www.craterapp.com").
+3. Neither the name nor any trademark of the Author may be used to
+endorse or promote products derived from this software without specific
+prior written permission.
+4. Users are entirely responsible, to the exclusion of the Author and
+any other persons, for compliance with (1) regulations set by owners or
+administrators of employed equipment, (2) licensing terms of any other
+software, and (3) local regulations regarding use, including those
+regarding import, export, and use of encryption software.
+
+THIS FREE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR OR ANY CONTRIBUTOR BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+EFFECTS OF UNAUTHORIZED OR MALICIOUS NETWORK ACCESS;
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..5fe89800
--- /dev/null
+++ b/README.md
@@ -0,0 +1,36 @@
+
+
+## Introduction
+
+Crater is an open-source web & mobile app that helps you track expenses, payments & create professional invoices & estimates.
+
+This repository contains the source code for the mobile app clients for [Crater](https://craterapp.com).
+
+Its built with Expo (React Native).
+
+# Table of Contents
+
+1. [Installation](#installation)
+2. [Web Version Link](#web)
+5. [Mobile App Links](#mobile-app-links)
+6. [License](#license)
+
+## Installation
+Below are the steps for starting up the crater app locally for development. If you aren't looking to customise or contribute the mobile apps then you can ignore the steps below and use the Crater [IOS & Android Apps](#mobile-app-links) directly.
+
+- Clone this repository
+- Install Expo CLI : `npm install -g expo-cli`
+- Change your current working directly in terminal to the cloned folder: `cd crater`
+- run command: `npm start`
+
+## Web
+- [Repository Link](https://github.com/bytefuryco/crater)
+- [Download Link](https://craterapp.com/downloads)
+
+## Mobile App Links
+- [Android](https://play.google.com/store/apps/details?id=com.craterapp.app)
+- IOS - Coming Soon
+
+## License
+Crater is released under the Attribution Assurance License.
+See [LICENSE](LICENSE) for details.
\ No newline at end of file
diff --git a/app.json b/app.json
new file mode 100644
index 00000000..cb8cbe59
--- /dev/null
+++ b/app.json
@@ -0,0 +1,36 @@
+{
+ "expo": {
+ "name": "Crater",
+ "slug": "crater",
+ "privacy": "public",
+ "sdkVersion": "34.0.0",
+ "platforms": [
+ "ios",
+ "android",
+ "web"
+ ],
+ "version": "1.0.1",
+ "orientation": "portrait",
+ "icon": "./assets/icon.png",
+ "splash": {
+ "image": "./assets/splash.png",
+ "resizeMode": "cover",
+ "backgroundColor": "#ffffff"
+ },
+ "updates": {
+ "fallbackToCacheTimeout": 0
+ },
+ "assetBundlePatterns": [
+ "**/*"
+ ],
+ "ios": {
+ "supportsTablet": true,
+ "bundleIdentifier": "com.craterapp.app"
+ },
+ "android": {
+ "package": "com.craterapp.app",
+ "versionCode": 4
+ },
+ "description": ""
+ }
+}
\ No newline at end of file
diff --git a/assets/crater-app-icon.png b/assets/crater-app-icon.png
new file mode 100644
index 00000000..1895bf68
Binary files /dev/null and b/assets/crater-app-icon.png differ
diff --git a/assets/icon.png b/assets/icon.png
new file mode 100644
index 00000000..371c81e4
Binary files /dev/null and b/assets/icon.png differ
diff --git a/assets/splash.png b/assets/splash.png
new file mode 100644
index 00000000..43498b9a
Binary files /dev/null and b/assets/splash.png differ
diff --git a/assets/splash2.png b/assets/splash2.png
new file mode 100644
index 00000000..4f9ade69
Binary files /dev/null and b/assets/splash2.png differ
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 00000000..2900afe9
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function(api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/config/index.js b/config/index.js
new file mode 100644
index 00000000..a665f178
--- /dev/null
+++ b/config/index.js
@@ -0,0 +1,15 @@
+
+// alert(process.env.ENDPOINT_API);
+
+module.exports = {
+ ENDPOINT_API: 'http://crater-web.test/api/',
+ ENDPOINT_URL: 'http://crater-web.test/',
+ // API_URL: process.env.ENDPOINT_API || ENDPOINT_API,
+ APP_VERSION: '1.0.0',
+
+ /*
+ * Sign in with google
+ GOOGLE_ANDROID_CLIENT_ID : '1068445967958-5od7lgi1lgfi3kfm12h076cp38r213j8.apps.googleusercontent.com' ,
+ GOOGLE_IOS_CLIENT_ID :
+ '1068445967958-j0cidm4m5073nqo3p12mqprum2vubuk3.apps.googleusercontent.com' */
+};
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..1089bc16
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,8270 @@
+{
+ "requires": true,
+ "lockfileVersion": 1,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
+ "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
+ "requires": {
+ "@babel/highlight": "^7.0.0"
+ }
+ },
+ "@babel/core": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.5.5.tgz",
+ "integrity": "sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==",
+ "requires": {
+ "@babel/code-frame": "^7.5.5",
+ "@babel/generator": "^7.5.5",
+ "@babel/helpers": "^7.5.5",
+ "@babel/parser": "^7.5.5",
+ "@babel/template": "^7.4.4",
+ "@babel/traverse": "^7.5.5",
+ "@babel/types": "^7.5.5",
+ "convert-source-map": "^1.1.0",
+ "debug": "^4.1.0",
+ "json5": "^2.1.0",
+ "lodash": "^4.17.13",
+ "resolve": "^1.3.2",
+ "semver": "^5.4.1",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "@babel/generator": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz",
+ "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==",
+ "requires": {
+ "@babel/types": "^7.5.5",
+ "jsesc": "^2.5.1",
+ "lodash": "^4.17.13",
+ "source-map": "^0.5.0",
+ "trim-right": "^1.0.1"
+ }
+ },
+ "@babel/helper-annotate-as-pure": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz",
+ "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==",
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-builder-binary-assignment-operator-visitor": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz",
+ "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==",
+ "requires": {
+ "@babel/helper-explode-assignable-expression": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-builder-react-jsx": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.3.0.tgz",
+ "integrity": "sha512-MjA9KgwCuPEkQd9ncSXvSyJ5y+j2sICHyrI0M3L+6fnS4wMSNDc1ARXsbTfbb2cXHn17VisSnU/sHFTCxVxSMw==",
+ "requires": {
+ "@babel/types": "^7.3.0",
+ "esutils": "^2.0.0"
+ }
+ },
+ "@babel/helper-call-delegate": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz",
+ "integrity": "sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==",
+ "requires": {
+ "@babel/helper-hoist-variables": "^7.4.4",
+ "@babel/traverse": "^7.4.4",
+ "@babel/types": "^7.4.4"
+ }
+ },
+ "@babel/helper-create-class-features-plugin": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.5.tgz",
+ "integrity": "sha512-ZsxkyYiRA7Bg+ZTRpPvB6AbOFKTFFK4LrvTet8lInm0V468MWCaSYJE+I7v2z2r8KNLtYiV+K5kTCnR7dvyZjg==",
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-member-expression-to-functions": "^7.5.5",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-replace-supers": "^7.5.5",
+ "@babel/helper-split-export-declaration": "^7.4.4"
+ }
+ },
+ "@babel/helper-define-map": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz",
+ "integrity": "sha512-fTfxx7i0B5NJqvUOBBGREnrqbTxRh7zinBANpZXAVDlsZxYdclDp467G1sQ8VZYMnAURY3RpBUAgOYT9GfzHBg==",
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/types": "^7.5.5",
+ "lodash": "^4.17.13"
+ }
+ },
+ "@babel/helper-explode-assignable-expression": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz",
+ "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==",
+ "requires": {
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz",
+ "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==",
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.0.0",
+ "@babel/template": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz",
+ "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==",
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz",
+ "integrity": "sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==",
+ "requires": {
+ "@babel/types": "^7.4.4"
+ }
+ },
+ "@babel/helper-member-expression-to-functions": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz",
+ "integrity": "sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA==",
+ "requires": {
+ "@babel/types": "^7.5.5"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz",
+ "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==",
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz",
+ "integrity": "sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/helper-simple-access": "^7.1.0",
+ "@babel/helper-split-export-declaration": "^7.4.4",
+ "@babel/template": "^7.4.4",
+ "@babel/types": "^7.5.5",
+ "lodash": "^4.17.13"
+ }
+ },
+ "@babel/helper-optimise-call-expression": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz",
+ "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==",
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz",
+ "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA=="
+ },
+ "@babel/helper-regex": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.5.5.tgz",
+ "integrity": "sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==",
+ "requires": {
+ "lodash": "^4.17.13"
+ }
+ },
+ "@babel/helper-remap-async-to-generator": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz",
+ "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==",
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.0.0",
+ "@babel/helper-wrap-function": "^7.1.0",
+ "@babel/template": "^7.1.0",
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz",
+ "integrity": "sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg==",
+ "requires": {
+ "@babel/helper-member-expression-to-functions": "^7.5.5",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/traverse": "^7.5.5",
+ "@babel/types": "^7.5.5"
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz",
+ "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==",
+ "requires": {
+ "@babel/template": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz",
+ "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==",
+ "requires": {
+ "@babel/types": "^7.4.4"
+ }
+ },
+ "@babel/helper-wrap-function": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz",
+ "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==",
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/template": "^7.1.0",
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.2.0"
+ }
+ },
+ "@babel/helpers": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.5.5.tgz",
+ "integrity": "sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g==",
+ "requires": {
+ "@babel/template": "^7.4.4",
+ "@babel/traverse": "^7.5.5",
+ "@babel/types": "^7.5.5"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
+ "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==",
+ "requires": {
+ "chalk": "^2.0.0",
+ "esutils": "^2.0.2",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz",
+ "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g=="
+ },
+ "@babel/plugin-external-helpers": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-external-helpers/-/plugin-external-helpers-7.2.0.tgz",
+ "integrity": "sha512-QFmtcCShFkyAsNtdCM3lJPmRe1iB+vPZymlB4LnDIKEBj2yKQLQKtoxXxJ8ePT5fwMl4QGg303p4mB0UsSI2/g==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-proposal-async-generator-functions": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz",
+ "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-remap-async-to-generator": "^7.1.0",
+ "@babel/plugin-syntax-async-generators": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-class-properties": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz",
+ "integrity": "sha512-AF79FsnWFxjlaosgdi421vmYG6/jg79bVD0dpD44QdgobzHKuLZ6S3vl8la9qIeSwGi8i1fS0O1mfuDAAdo1/A==",
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.5.5",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-proposal-decorators": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.4.tgz",
+ "integrity": "sha512-z7MpQz3XC/iQJWXH9y+MaWcLPNSMY9RQSthrLzak8R8hCj0fuyNk+Dzi9kfNe/JxxlWQ2g7wkABbgWjW36MTcw==",
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.4.4",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-decorators": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-dynamic-import": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz",
+ "integrity": "sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-dynamic-import": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-export-default-from": {
+ "version": "7.5.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.5.2.tgz",
+ "integrity": "sha512-wr9Itk05L1/wyyZKVEmXWCdcsp/e185WUNl6AfYZeEKYaUPPvHXRDqO5K1VH7/UamYqGJowFRuCv30aDYZawsg==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-export-default-from": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-json-strings": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz",
+ "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-json-strings": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-nullish-coalescing-operator": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.4.4.tgz",
+ "integrity": "sha512-Amph7Epui1Dh/xxUxS2+K22/MUi6+6JVTvy3P58tja3B6yKTSjwwx0/d83rF7551D6PVSSoplQb8GCwqec7HRw==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-object-rest-spread": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz",
+ "integrity": "sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-object-rest-spread": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-optional-catch-binding": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz",
+ "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-optional-chaining": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.2.0.tgz",
+ "integrity": "sha512-ea3Q6edZC/55wEBVZAEz42v528VulyO0eir+7uky/sT4XRcdkWJcFi1aPtitTlwUzGnECWJNExWww1SStt+yWw==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-optional-chaining": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-unicode-property-regex": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz",
+ "integrity": "sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-regex": "^7.4.4",
+ "regexpu-core": "^4.5.4"
+ }
+ },
+ "@babel/plugin-syntax-async-generators": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz",
+ "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-class-properties": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.2.0.tgz",
+ "integrity": "sha512-UxYaGXYQ7rrKJS/PxIKRkv3exi05oH7rokBAsmCSsCxz1sVPZ7Fu6FzKoGgUvmY+0YgSkYHgUoCh5R5bCNBQlw==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-decorators": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz",
+ "integrity": "sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-dynamic-import": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz",
+ "integrity": "sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-export-default-from": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.2.0.tgz",
+ "integrity": "sha512-c7nqUnNST97BWPtoe+Ssi+fJukc9P9/JMZ71IOMNQWza2E+Psrd46N6AEvtw6pqK+gt7ChjXyrw4SPDO79f3Lw==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-flow": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.2.0.tgz",
+ "integrity": "sha512-r6YMuZDWLtLlu0kqIim5o/3TNRAlWb073HwT3e2nKf9I8IIvOggPrnILYPsrrKilmn/mYEMCf/Z07w3yQJF6dg==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-json-strings": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz",
+ "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-jsx": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz",
+ "integrity": "sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.2.0.tgz",
+ "integrity": "sha512-lRCEaKE+LTxDQtgbYajI04ddt6WW0WJq57xqkAZ+s11h4YgfRHhVA/Y2VhfPzzFD4qeLHWg32DMp9HooY4Kqlg==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz",
+ "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz",
+ "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-optional-chaining": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.2.0.tgz",
+ "integrity": "sha512-HtGCtvp5Uq/jH/WNUPkK6b7rufnCPLLlDAFN7cmACoIjaOOiXxUt3SswU5loHqrhtqTsa/WoLQ1OQ1AGuZqaWA==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-typescript": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz",
+ "integrity": "sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-arrow-functions": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz",
+ "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-async-to-generator": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz",
+ "integrity": "sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-remap-async-to-generator": "^7.1.0"
+ }
+ },
+ "@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz",
+ "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-block-scoping": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.5.5.tgz",
+ "integrity": "sha512-82A3CLRRdYubkG85lKwhZB0WZoHxLGsJdux/cOVaJCJpvYFl1LVzAIFyRsa7CvXqW8rBM4Zf3Bfn8PHt5DP0Sg==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "lodash": "^4.17.13"
+ }
+ },
+ "@babel/plugin-transform-classes": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz",
+ "integrity": "sha512-U2htCNK/6e9K7jGyJ++1p5XRU+LJjrwtoiVn9SzRlDT2KubcZ11OOwy3s24TjHxPgxNwonCYP7U2K51uVYCMDg==",
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.0.0",
+ "@babel/helper-define-map": "^7.5.5",
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-replace-supers": "^7.5.5",
+ "@babel/helper-split-export-declaration": "^7.4.4",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/plugin-transform-computed-properties": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz",
+ "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-destructuring": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz",
+ "integrity": "sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-dotall-regex": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz",
+ "integrity": "sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-regex": "^7.4.4",
+ "regexpu-core": "^4.5.4"
+ }
+ },
+ "@babel/plugin-transform-duplicate-keys": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz",
+ "integrity": "sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz",
+ "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==",
+ "requires": {
+ "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-flow-strip-types": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.4.4.tgz",
+ "integrity": "sha512-WyVedfeEIILYEaWGAUWzVNyqG4sfsNooMhXWsu/YzOvVGcsnPb5PguysjJqI3t3qiaYj0BR8T2f5njdjTGe44Q==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-flow": "^7.2.0"
+ }
+ },
+ "@babel/plugin-transform-for-of": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz",
+ "integrity": "sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-function-name": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz",
+ "integrity": "sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==",
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-literals": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz",
+ "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-member-expression-literals": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz",
+ "integrity": "sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-modules-amd": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz",
+ "integrity": "sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg==",
+ "requires": {
+ "@babel/helper-module-transforms": "^7.1.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "babel-plugin-dynamic-import-node": "^2.3.0"
+ }
+ },
+ "@babel/plugin-transform-modules-commonjs": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz",
+ "integrity": "sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ==",
+ "requires": {
+ "@babel/helper-module-transforms": "^7.4.4",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-simple-access": "^7.1.0",
+ "babel-plugin-dynamic-import-node": "^2.3.0"
+ }
+ },
+ "@babel/plugin-transform-modules-systemjs": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz",
+ "integrity": "sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg==",
+ "requires": {
+ "@babel/helper-hoist-variables": "^7.4.4",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "babel-plugin-dynamic-import-node": "^2.3.0"
+ }
+ },
+ "@babel/plugin-transform-modules-umd": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz",
+ "integrity": "sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==",
+ "requires": {
+ "@babel/helper-module-transforms": "^7.1.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.4.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz",
+ "integrity": "sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg==",
+ "requires": {
+ "regexp-tree": "^0.1.6"
+ }
+ },
+ "@babel/plugin-transform-new-target": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz",
+ "integrity": "sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-object-assign": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.2.0.tgz",
+ "integrity": "sha512-nmE55cZBPFgUktbF2OuoZgPRadfxosLOpSgzEPYotKSls9J4pEPcembi8r78RU37Rph6UApCpNmsQA4QMWK9Ng==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-object-super": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz",
+ "integrity": "sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-replace-supers": "^7.5.5"
+ }
+ },
+ "@babel/plugin-transform-parameters": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz",
+ "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==",
+ "requires": {
+ "@babel/helper-call-delegate": "^7.4.4",
+ "@babel/helper-get-function-arity": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-property-literals": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz",
+ "integrity": "sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-react-display-name": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz",
+ "integrity": "sha512-Htf/tPa5haZvRMiNSQSFifK12gtr/8vwfr+A9y69uF0QcU77AVu4K7MiHEkTxF7lQoHOL0F9ErqgfNEAKgXj7A==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-react-jsx": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.3.0.tgz",
+ "integrity": "sha512-a/+aRb7R06WcKvQLOu4/TpjKOdvVEKRLWFpKcNuHhiREPgGRB4TQJxq07+EZLS8LFVYpfq1a5lDUnuMdcCpBKg==",
+ "requires": {
+ "@babel/helper-builder-react-jsx": "^7.3.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-jsx": "^7.2.0"
+ }
+ },
+ "@babel/plugin-transform-react-jsx-source": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.5.0.tgz",
+ "integrity": "sha512-58Q+Jsy4IDCZx7kqEZuSDdam/1oW8OdDX8f+Loo6xyxdfg1yF0GE2XNJQSTZCaMol93+FBzpWiPEwtbMloAcPg==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-jsx": "^7.2.0"
+ }
+ },
+ "@babel/plugin-transform-regenerator": {
+ "version": "7.4.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz",
+ "integrity": "sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==",
+ "requires": {
+ "regenerator-transform": "^0.14.0"
+ }
+ },
+ "@babel/plugin-transform-reserved-words": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz",
+ "integrity": "sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-runtime": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.5.tgz",
+ "integrity": "sha512-6Xmeidsun5rkwnGfMOp6/z9nSzWpHFNVr2Jx7kwoq4mVatQfQx5S56drBgEHF+XQbKOdIaOiMIINvp/kAwMN+w==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "resolve": "^1.8.1",
+ "semver": "^5.5.1"
+ }
+ },
+ "@babel/plugin-transform-shorthand-properties": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz",
+ "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-spread": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz",
+ "integrity": "sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-sticky-regex": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz",
+ "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-regex": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-template-literals": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz",
+ "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==",
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-typeof-symbol": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz",
+ "integrity": "sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-typescript": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.5.5.tgz",
+ "integrity": "sha512-pehKf4m640myZu5B2ZviLaiBlxMCjSZ1qTEO459AXKX5GnPueyulJeCqZFs1nz/Ya2dDzXQ1NxZ/kKNWyD4h6w==",
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.5.5",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-typescript": "^7.2.0"
+ }
+ },
+ "@babel/plugin-transform-unicode-regex": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz",
+ "integrity": "sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA==",
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-regex": "^7.4.4",
+ "regexpu-core": "^4.5.4"
+ }
+ },
+ "@babel/preset-env": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.5.5.tgz",
+ "integrity": "sha512-GMZQka/+INwsMz1A5UEql8tG015h5j/qjptpKY2gJ7giy8ohzU710YciJB5rcKsWGWHiW3RUnHib0E5/m3Tp3A==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-proposal-async-generator-functions": "^7.2.0",
+ "@babel/plugin-proposal-dynamic-import": "^7.5.0",
+ "@babel/plugin-proposal-json-strings": "^7.2.0",
+ "@babel/plugin-proposal-object-rest-spread": "^7.5.5",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.2.0",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
+ "@babel/plugin-syntax-async-generators": "^7.2.0",
+ "@babel/plugin-syntax-dynamic-import": "^7.2.0",
+ "@babel/plugin-syntax-json-strings": "^7.2.0",
+ "@babel/plugin-syntax-object-rest-spread": "^7.2.0",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.2.0",
+ "@babel/plugin-transform-arrow-functions": "^7.2.0",
+ "@babel/plugin-transform-async-to-generator": "^7.5.0",
+ "@babel/plugin-transform-block-scoped-functions": "^7.2.0",
+ "@babel/plugin-transform-block-scoping": "^7.5.5",
+ "@babel/plugin-transform-classes": "^7.5.5",
+ "@babel/plugin-transform-computed-properties": "^7.2.0",
+ "@babel/plugin-transform-destructuring": "^7.5.0",
+ "@babel/plugin-transform-dotall-regex": "^7.4.4",
+ "@babel/plugin-transform-duplicate-keys": "^7.5.0",
+ "@babel/plugin-transform-exponentiation-operator": "^7.2.0",
+ "@babel/plugin-transform-for-of": "^7.4.4",
+ "@babel/plugin-transform-function-name": "^7.4.4",
+ "@babel/plugin-transform-literals": "^7.2.0",
+ "@babel/plugin-transform-member-expression-literals": "^7.2.0",
+ "@babel/plugin-transform-modules-amd": "^7.5.0",
+ "@babel/plugin-transform-modules-commonjs": "^7.5.0",
+ "@babel/plugin-transform-modules-systemjs": "^7.5.0",
+ "@babel/plugin-transform-modules-umd": "^7.2.0",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.5",
+ "@babel/plugin-transform-new-target": "^7.4.4",
+ "@babel/plugin-transform-object-super": "^7.5.5",
+ "@babel/plugin-transform-parameters": "^7.4.4",
+ "@babel/plugin-transform-property-literals": "^7.2.0",
+ "@babel/plugin-transform-regenerator": "^7.4.5",
+ "@babel/plugin-transform-reserved-words": "^7.2.0",
+ "@babel/plugin-transform-shorthand-properties": "^7.2.0",
+ "@babel/plugin-transform-spread": "^7.2.0",
+ "@babel/plugin-transform-sticky-regex": "^7.2.0",
+ "@babel/plugin-transform-template-literals": "^7.4.4",
+ "@babel/plugin-transform-typeof-symbol": "^7.2.0",
+ "@babel/plugin-transform-unicode-regex": "^7.4.4",
+ "@babel/types": "^7.5.5",
+ "browserslist": "^4.6.0",
+ "core-js-compat": "^3.1.1",
+ "invariant": "^2.2.2",
+ "js-levenshtein": "^1.1.3",
+ "semver": "^5.5.0"
+ }
+ },
+ "@babel/register": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.5.5.tgz",
+ "integrity": "sha512-pdd5nNR+g2qDkXZlW1yRCWFlNrAn2PPdnZUB72zjX4l1Vv4fMRRLwyf+n/idFCLI1UgVGboUU8oVziwTBiyNKQ==",
+ "requires": {
+ "core-js": "^3.0.0",
+ "find-cache-dir": "^2.0.0",
+ "lodash": "^4.17.13",
+ "mkdirp": "^0.5.1",
+ "pirates": "^4.0.0",
+ "source-map-support": "^0.5.9"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.1.tgz",
+ "integrity": "sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw=="
+ }
+ }
+ },
+ "@babel/runtime": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz",
+ "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==",
+ "requires": {
+ "regenerator-runtime": "^0.13.2"
+ },
+ "dependencies": {
+ "regenerator-runtime": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
+ "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
+ }
+ }
+ },
+ "@babel/template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz",
+ "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==",
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/parser": "^7.4.4",
+ "@babel/types": "^7.4.4"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz",
+ "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==",
+ "requires": {
+ "@babel/code-frame": "^7.5.5",
+ "@babel/generator": "^7.5.5",
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-split-export-declaration": "^7.4.4",
+ "@babel/parser": "^7.5.5",
+ "@babel/types": "^7.5.5",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.13"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "@babel/types": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz",
+ "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==",
+ "requires": {
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.13",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@expo/vector-icons": {
+ "version": "10.0.5",
+ "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-10.0.5.tgz",
+ "integrity": "sha512-SWdAx2Qzxp5TgT3hZEoF/KHnaDW7ajIFztrDdaDZl3nPo7ExK0YiQ03V0z0xMd+uQwl3SZO3JMwPZ7YnuxcMEg==",
+ "requires": {
+ "lodash": "^4.17.4"
+ }
+ },
+ "@expo/websql": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@expo/websql/-/websql-1.0.1.tgz",
+ "integrity": "sha1-//DPnBuqH3D54dZYt8OaQg2bEKk=",
+ "requires": {
+ "argsarray": "^0.0.1",
+ "immediate": "^3.2.2",
+ "noop-fn": "^1.0.0",
+ "pouchdb-collections": "^1.0.1",
+ "tiny-queue": "^0.2.1"
+ }
+ },
+ "@react-native-community/eslint-config": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/@react-native-community/eslint-config/-/eslint-config-0.0.5.tgz",
+ "integrity": "sha512-jwO2tnKaTPTLX5XYXMHGEnFdf543SU7jz98/OF5mDH3b7lP+BOaCD+jVfqqHoDRkcqyPlYiR1CgwVGWpi0vMWg==",
+ "requires": {
+ "@typescript-eslint/eslint-plugin": "^1.5.0",
+ "@typescript-eslint/parser": "^1.5.0",
+ "babel-eslint": "10.0.1",
+ "eslint-plugin-eslint-comments": "^3.1.1",
+ "eslint-plugin-flowtype": "2.50.3",
+ "eslint-plugin-jest": "22.4.1",
+ "eslint-plugin-prettier": "2.6.2",
+ "eslint-plugin-react": "7.12.4",
+ "eslint-plugin-react-hooks": "^1.5.1",
+ "eslint-plugin-react-native": "3.6.0",
+ "prettier": "1.16.4"
+ },
+ "dependencies": {
+ "babel-eslint": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz",
+ "integrity": "sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==",
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/parser": "^7.0.0",
+ "@babel/traverse": "^7.0.0",
+ "@babel/types": "^7.0.0",
+ "eslint-scope": "3.7.1",
+ "eslint-visitor-keys": "^1.0.0"
+ }
+ },
+ "eslint-plugin-flowtype": {
+ "version": "2.50.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.50.3.tgz",
+ "integrity": "sha512-X+AoKVOr7Re0ko/yEXyM5SSZ0tazc6ffdIOocp2fFUlWoDt7DV0Bz99mngOkAFLOAWjqRA5jPwqUCbrx13XoxQ==",
+ "requires": {
+ "lodash": "^4.17.10"
+ }
+ },
+ "eslint-plugin-prettier": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.2.tgz",
+ "integrity": "sha512-tGek5clmW5swrAx1mdPYM8oThrBE83ePh7LeseZHBWfHVGrHPhKn7Y5zgRMbU/9D5Td9K4CEmUPjGxA7iw98Og==",
+ "requires": {
+ "fast-diff": "^1.1.1",
+ "jest-docblock": "^21.0.0"
+ }
+ },
+ "eslint-scope": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
+ "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "prettier": {
+ "version": "1.16.4",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz",
+ "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g=="
+ }
+ }
+ },
+ "@react-native-community/netinfo": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-4.6.0.tgz",
+ "integrity": "sha512-wz39BUpExDU1kTpLlBkDwwb0Efg+uuwixToosTSarZgpzG/CmcRvWdD786TMiE5tLDd+Mpi2xh3w4FrVM8zjoA=="
+ },
+ "@react-navigation/core": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-3.5.0.tgz",
+ "integrity": "sha512-NLm24lA51R8o8c+iFnwtN9elqRzm4OJ8f1qPBCUNIYW1sb8M5yCD53vRP0fRcPFpr/6Xzs2TJMsWnnebwFp0Rw==",
+ "requires": {
+ "hoist-non-react-statics": "^3.3.0",
+ "path-to-regexp": "^1.7.0",
+ "query-string": "^6.4.2",
+ "react-is": "^16.8.6"
+ }
+ },
+ "@react-navigation/native": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-3.6.0.tgz",
+ "integrity": "sha512-MmsEF4Gf3DD7rtZlbOLULQOBCxE2nGz6FdPYxtlEI+N/E2I5OrdxrTNAA7EGWsIiaJEapU4bp0a+tLjh/9PQpA==",
+ "requires": {
+ "hoist-non-react-statics": "^3.0.1",
+ "react-native-safe-area-view": "^0.14.1",
+ "react-native-screens": "^1.0.0 || ^1.0.0-alpha"
+ }
+ },
+ "@redux-saga/core": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.0.3.tgz",
+ "integrity": "sha512-zf8h5N0oTzaNeSMxOWH9GJMB9IRSM8JubDsrZVsvVltXjzFFSR8DNt7tbPoRJUK0hFfQB1it+bL+dEMWpD7wXA==",
+ "requires": {
+ "@babel/runtime": "^7.0.0",
+ "@redux-saga/deferred": "^1.0.1",
+ "@redux-saga/delay-p": "^1.0.1",
+ "@redux-saga/is": "^1.0.2",
+ "@redux-saga/symbols": "^1.0.1",
+ "@redux-saga/types": "^1.0.2",
+ "redux": ">=0.10 <5",
+ "typescript-tuple": "^2.1.0"
+ }
+ },
+ "@redux-saga/deferred": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@redux-saga/deferred/-/deferred-1.0.1.tgz",
+ "integrity": "sha512-+gW5xQ93QXOOmRLAmX8x2Hx1HpbTG6CM6+HcdTSbJovh4uMWaGyeDECnqXSt8QqA/ja3s2nqYXLqXFKepIQ1hw=="
+ },
+ "@redux-saga/delay-p": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@redux-saga/delay-p/-/delay-p-1.0.1.tgz",
+ "integrity": "sha512-0SnNDyDLUyB4NThtptAwiprNOnbCNhoed/Rp5JwS7SB+a/AdWynVgg/E6BmjsggLFNr07KW0bzn05tsPRBuU7Q==",
+ "requires": {
+ "@redux-saga/symbols": "^1.0.1"
+ }
+ },
+ "@redux-saga/is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@redux-saga/is/-/is-1.0.2.tgz",
+ "integrity": "sha512-WnaUOwYvPK2waWjzebT4uhL8zY76XNkzzpJ2EQJe8bN1tByvAjvT7MuJZTSshOhdHL5PsRO0MsH224XIXBJidQ==",
+ "requires": {
+ "@redux-saga/symbols": "^1.0.1",
+ "@redux-saga/types": "^1.0.2"
+ }
+ },
+ "@redux-saga/symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@redux-saga/symbols/-/symbols-1.0.1.tgz",
+ "integrity": "sha512-akKkzcVnb1RzJaZV2LQFbi51abvdICMuAKwwLoCjjxLbLAGIw9EJxk5ucNnWSSCEsoEQMeol5tkAcK+Xzuv1Bg=="
+ },
+ "@redux-saga/types": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.0.2.tgz",
+ "integrity": "sha512-8/qcMh15507AnXJ3lBeuhsdFwnWQqnp68EpUuHlYPixJ5vjVmls7/Jq48cnUlrZI8Jd9U1jkhfCl0gaT5KMgVw=="
+ },
+ "@types/eslint-visitor-keys": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+ "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag=="
+ },
+ "@types/fbemitter": {
+ "version": "2.0.32",
+ "resolved": "https://registry.npmjs.org/@types/fbemitter/-/fbemitter-2.0.32.tgz",
+ "integrity": "sha1-jtIE2g9U6cjq7DGx7skeJRMtCCw="
+ },
+ "@types/invariant": {
+ "version": "2.2.30",
+ "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.30.tgz",
+ "integrity": "sha512-98fB+yo7imSD2F7PF7GIpELNgtLNgo5wjivu0W5V4jx+KVVJxo6p/qN4zdzSTBWy4/sN3pPyXwnhRSD28QX+ag=="
+ },
+ "@types/json-schema": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz",
+ "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A=="
+ },
+ "@types/lodash": {
+ "version": "4.14.137",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.137.tgz",
+ "integrity": "sha512-g4rNK5SRKloO+sUGbuO7aPtwbwzMgjK+bm9BBhLD7jGUiGR7zhwYEhSln/ihgYQBeIJ5j7xjyaYzrWTcu3UotQ=="
+ },
+ "@types/lodash.zipobject": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@types/lodash.zipobject/-/lodash.zipobject-4.1.6.tgz",
+ "integrity": "sha512-30khEHqHWaLgMZR35wtkg07OmHiNiDQyor0SK7oj8Sy05tg6jDjPmJybeZ64WKeFZUEgs1tdJwdT0xUl+2qUgQ==",
+ "requires": {
+ "@types/lodash": "*"
+ }
+ },
+ "@types/qs": {
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.5.3.tgz",
+ "integrity": "sha512-Jugo5V/1bS0fRhy2z8+cUAHEyWOATaz4rbyLVvcFs7+dXp5HfwpEwzF1Q11bB10ApUqHf+yTauxI0UXQDwGrbA=="
+ },
+ "@types/uuid-js": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/@types/uuid-js/-/uuid-js-0.7.2.tgz",
+ "integrity": "sha512-9R+mA6mMXkFVQnXEeX5fMQDR2SYND7cafJTqbeMpLhgsL7qr7MF4ZBxWpLexml3lZsBsyAmqVWbOiB0N10m15w=="
+ },
+ "@types/websql": {
+ "version": "0.0.27",
+ "resolved": "https://registry.npmjs.org/@types/websql/-/websql-0.0.27.tgz",
+ "integrity": "sha1-Yhpman8CAY58u0q6uVaiVzbCfXE="
+ },
+ "@typescript-eslint/eslint-plugin": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz",
+ "integrity": "sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==",
+ "requires": {
+ "@typescript-eslint/experimental-utils": "1.13.0",
+ "eslint-utils": "^1.3.1",
+ "functional-red-black-tree": "^1.0.1",
+ "regexpp": "^2.0.1",
+ "tsutils": "^3.7.0"
+ }
+ },
+ "@typescript-eslint/experimental-utils": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz",
+ "integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==",
+ "requires": {
+ "@types/json-schema": "^7.0.3",
+ "@typescript-eslint/typescript-estree": "1.13.0",
+ "eslint-scope": "^4.0.0"
+ },
+ "dependencies": {
+ "eslint-scope": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+ "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ }
+ }
+ },
+ "@typescript-eslint/parser": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz",
+ "integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==",
+ "requires": {
+ "@types/eslint-visitor-keys": "^1.0.0",
+ "@typescript-eslint/experimental-utils": "1.13.0",
+ "@typescript-eslint/typescript-estree": "1.13.0",
+ "eslint-visitor-keys": "^1.0.0"
+ }
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz",
+ "integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==",
+ "requires": {
+ "lodash.unescape": "4.0.1",
+ "semver": "5.5.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
+ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
+ }
+ }
+ },
+ "@unimodules/core": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@unimodules/core/-/core-3.0.2.tgz",
+ "integrity": "sha512-EMZjVp+yrtoPKpDBPvj4+hyDWALl7gvpWeUsDz2Nb9MMBPLnhag1uNk3KC98StJdnjbSXKSdKrCMMidOXnyKcg==",
+ "requires": {
+ "compare-versions": "^3.4.0"
+ }
+ },
+ "@unimodules/react-native-adapter": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@unimodules/react-native-adapter/-/react-native-adapter-3.0.0.tgz",
+ "integrity": "sha512-zkFFE0HQ2Flfx/aY3hBKDgMvQ1meUm3H6vMIacY1KywexCuKW8ivBobkOsHIet4jf7km0Eklt6WtB3LqQVw5yw==",
+ "requires": {
+ "invariant": "^2.2.4",
+ "lodash": "^4.5.0",
+ "prop-types": "^15.6.1"
+ }
+ },
+ "absolute-path": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/absolute-path/-/absolute-path-0.0.0.tgz",
+ "integrity": "sha1-p4di+9rftSl76ZsV01p4Wy8JW/c="
+ },
+ "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"
+ }
+ },
+ "acorn": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.0.0.tgz",
+ "integrity": "sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz",
+ "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
+ "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz",
+ "integrity": "sha1-DELU+xcWDVqa8eSEus4cZpIsGyE="
+ },
+ "ansi-colors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
+ "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
+ "requires": {
+ "ansi-wrap": "^0.1.0"
+ }
+ },
+ "ansi-cyan": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz",
+ "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=",
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-escapes": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+ "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="
+ },
+ "ansi-gray": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
+ "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=",
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-red": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz",
+ "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=",
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+ },
+ "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==",
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "ansi-wrap": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
+ "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768="
+ },
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ },
+ "dependencies": {
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
+ }
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ }
+ }
+ },
+ "are-we-there-yet": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+ "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "argsarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/argsarray/-/argsarray-0.0.1.tgz",
+ "integrity": "sha1-bnIHtOzbObCviDA/pa4ivajfYcs="
+ },
+ "arr-diff": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
+ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+ "requires": {
+ "arr-flatten": "^1.0.1"
+ }
+ },
+ "arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
+ },
+ "arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="
+ },
+ "array-filter": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
+ "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw="
+ },
+ "array-find-index": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E="
+ },
+ "array-includes": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
+ "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
+ "requires": {
+ "define-properties": "^1.1.2",
+ "es-abstract": "^1.7.0"
+ }
+ },
+ "array-map": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
+ "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI="
+ },
+ "array-reduce": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
+ "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys="
+ },
+ "array-slice": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
+ "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU="
+ },
+ "array-unique": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+ "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM="
+ },
+ "art": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/art/-/art-0.10.3.tgz",
+ "integrity": "sha512-HXwbdofRTiJT6qZX/FnchtldzJjS3vkLJxQilc3Xj+ma2MXjY4UAyQ0ls1XZYVnDvVIBiFZbC6QsvtW86TD6tQ=="
+ },
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+ },
+ "assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
+ },
+ "astral-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "dev": true
+ },
+ "async": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+ "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+ "requires": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "async-limiter": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
+ },
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
+ },
+ "babel-eslint": {
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.3.tgz",
+ "integrity": "sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==",
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/parser": "^7.0.0",
+ "@babel/traverse": "^7.0.0",
+ "@babel/types": "^7.0.0",
+ "eslint-visitor-keys": "^1.0.0",
+ "resolve": "^1.12.0"
+ }
+ },
+ "babel-plugin-dotenv": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-dotenv/-/babel-plugin-dotenv-0.1.1.tgz",
+ "integrity": "sha1-nI+upnp8A0/n6UCZGHqy51c0ALw=",
+ "dev": true,
+ "requires": {
+ "dotenv": "^2.0.0"
+ }
+ },
+ "babel-plugin-dynamic-import-node": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz",
+ "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==",
+ "requires": {
+ "object.assign": "^4.1.0"
+ }
+ },
+ "babel-plugin-module-resolver": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.2.0.tgz",
+ "integrity": "sha512-tjR0GvSndzPew/Iayf4uICWZqjBwnlMWjSx6brryfQ81F9rxBVqwDJtFCV8oOs0+vJeefK9TmdZtkIFdFe1UnA==",
+ "requires": {
+ "find-babel-config": "^1.1.0",
+ "glob": "^7.1.2",
+ "pkg-up": "^2.0.0",
+ "reselect": "^3.0.1",
+ "resolve": "^1.4.0"
+ }
+ },
+ "babel-plugin-react-native-web": {
+ "version": "0.11.7",
+ "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.11.7.tgz",
+ "integrity": "sha512-CxE7uhhqkzAFkwV2X7+Mc/UVPujQQDtja/EGxCXRJvdYRi72QTmaJYKbK1lV9qgTZuB+TDguU89coaA9Z1BNbg=="
+ },
+ "babel-plugin-syntax-trailing-function-commas": {
+ "version": "7.0.0-beta.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz",
+ "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ=="
+ },
+ "babel-preset-expo": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-6.0.0.tgz",
+ "integrity": "sha512-MvDy86afmCt4sFYkg7yXsZyGL0yONT5JQHZSK1r8cu26Zm1No0yQyll+w78e2OkkYwVFtC1u70GyBPdERG7BZg==",
+ "requires": {
+ "@babel/core": "^7.1.0",
+ "@babel/plugin-proposal-decorators": "^7.1.0",
+ "@babel/plugin-transform-modules-commonjs": "^7.4.4",
+ "@babel/preset-env": "^7.3.1",
+ "babel-plugin-module-resolver": "^3.1.1",
+ "babel-plugin-react-native-web": "^0.11.2",
+ "metro-react-native-babel-preset": "^0.51.1"
+ }
+ },
+ "babel-preset-fbjs": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.2.0.tgz",
+ "integrity": "sha512-5Jo+JeWiVz2wHUUyAlvb/sSYnXNig9r+HqGAOSfh5Fzxp7SnAaR/tEGRJ1ZX7C77kfk82658w6R5Z+uPATTD9g==",
+ "requires": {
+ "@babel/plugin-proposal-class-properties": "^7.0.0",
+ "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
+ "@babel/plugin-syntax-class-properties": "^7.0.0",
+ "@babel/plugin-syntax-flow": "^7.0.0",
+ "@babel/plugin-syntax-jsx": "^7.0.0",
+ "@babel/plugin-syntax-object-rest-spread": "^7.0.0",
+ "@babel/plugin-transform-arrow-functions": "^7.0.0",
+ "@babel/plugin-transform-block-scoped-functions": "^7.0.0",
+ "@babel/plugin-transform-block-scoping": "^7.0.0",
+ "@babel/plugin-transform-classes": "^7.0.0",
+ "@babel/plugin-transform-computed-properties": "^7.0.0",
+ "@babel/plugin-transform-destructuring": "^7.0.0",
+ "@babel/plugin-transform-flow-strip-types": "^7.0.0",
+ "@babel/plugin-transform-for-of": "^7.0.0",
+ "@babel/plugin-transform-function-name": "^7.0.0",
+ "@babel/plugin-transform-literals": "^7.0.0",
+ "@babel/plugin-transform-member-expression-literals": "^7.0.0",
+ "@babel/plugin-transform-modules-commonjs": "^7.0.0",
+ "@babel/plugin-transform-object-super": "^7.0.0",
+ "@babel/plugin-transform-parameters": "^7.0.0",
+ "@babel/plugin-transform-property-literals": "^7.0.0",
+ "@babel/plugin-transform-react-display-name": "^7.0.0",
+ "@babel/plugin-transform-react-jsx": "^7.0.0",
+ "@babel/plugin-transform-shorthand-properties": "^7.0.0",
+ "@babel/plugin-transform-spread": "^7.0.0",
+ "@babel/plugin-transform-template-literals": "^7.0.0",
+ "babel-plugin-syntax-trailing-function-commas": "^7.0.0-beta.0"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+ },
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "requires": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
+ }
+ }
+ },
+ "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=="
+ },
+ "basic-auth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "requires": {
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "big-integer": {
+ "version": "1.6.44",
+ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.44.tgz",
+ "integrity": "sha512-7MzElZPTyJ2fNvBkPxtFQ2fWIkVmuzw41+BZHSzpEq3ymB2MfeKp1+yXl/tS75xCx+WnyV+yb0kp+K1C3UNwmQ=="
+ },
+ "blueimp-md5": {
+ "version": "2.11.1",
+ "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.11.1.tgz",
+ "integrity": "sha512-4UiOAmql2XO0Sws07OVzYdCKK0K2Va5g6AVgYXoGhEQiKrdSOefjUCm1frPk6E+xiIOHRqaFg+TUGo7cClKg5g=="
+ },
+ "bplist-creator": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.7.tgz",
+ "integrity": "sha1-N98VNgkoJLh8QvlXsBNEEXNyrkU=",
+ "requires": {
+ "stream-buffers": "~2.2.0"
+ }
+ },
+ "bplist-parser": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.1.1.tgz",
+ "integrity": "sha1-1g1dzCDLptx+HymbNdPh+V2vuuY=",
+ "requires": {
+ "big-integer": "^1.6.7"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
+ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
+ "requires": {
+ "expand-range": "^1.8.1",
+ "preserve": "^0.2.0",
+ "repeat-element": "^1.1.2"
+ }
+ },
+ "browserslist": {
+ "version": "4.6.6",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.6.tgz",
+ "integrity": "sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA==",
+ "requires": {
+ "caniuse-lite": "^1.0.30000984",
+ "electron-to-chromium": "^1.3.191",
+ "node-releases": "^1.1.25"
+ }
+ },
+ "bser": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.0.tgz",
+ "integrity": "sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg==",
+ "requires": {
+ "node-int64": "^0.4.0"
+ }
+ },
+ "buffer-alloc": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
+ "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
+ "requires": {
+ "buffer-alloc-unsafe": "^1.1.0",
+ "buffer-fill": "^1.0.0"
+ }
+ },
+ "buffer-alloc-unsafe": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
+ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
+ },
+ "buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
+ },
+ "buffer-fill": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
+ "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
+ },
+ "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=="
+ },
+ "bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+ },
+ "cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "requires": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ }
+ },
+ "caller-callsite": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
+ "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+ "requires": {
+ "callsites": "^2.0.0"
+ }
+ },
+ "caller-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
+ "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+ "requires": {
+ "caller-callsite": "^2.0.0"
+ }
+ },
+ "callsites": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+ "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA="
+ },
+ "camelcase": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
+ },
+ "caniuse-lite": {
+ "version": "1.0.30000989",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz",
+ "integrity": "sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw=="
+ },
+ "capture-exit": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-1.2.0.tgz",
+ "integrity": "sha1-HF/MSJ/QqwDU8ax64QcuMXP7q28=",
+ "requires": {
+ "rsvp": "^3.3.3"
+ }
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chardet": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
+ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I="
+ },
+ "class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "requires": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "cli-cursor": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+ "requires": {
+ "restore-cursor": "^2.0.0"
+ }
+ },
+ "cli-width": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk="
+ },
+ "cliui": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wrap-ansi": "^2.0.0"
+ },
+ "dependencies": {
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ }
+ }
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+ },
+ "collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "requires": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ }
+ },
+ "color": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz",
+ "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==",
+ "requires": {
+ "color-convert": "^1.9.1",
+ "color-string": "^1.5.2"
+ }
+ },
+ "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==",
+ "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="
+ },
+ "color-string": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
+ "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
+ "requires": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
+ },
+ "commander": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ=="
+ },
+ "commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
+ },
+ "compare-versions": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.5.1.tgz",
+ "integrity": "sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg=="
+ },
+ "component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
+ },
+ "compressible": {
+ "version": "2.0.17",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz",
+ "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==",
+ "requires": {
+ "mime-db": ">= 1.40.0 < 2"
+ }
+ },
+ "compression": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+ "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+ "requires": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.16",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.2",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "connect": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+ "requires": {
+ "debug": "2.6.9",
+ "finalhandler": "1.1.2",
+ "parseurl": "~1.3.3",
+ "utils-merge": "1.0.1"
+ }
+ },
+ "convert-source-map": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
+ "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==",
+ "requires": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
+ },
+ "core-js": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz",
+ "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A=="
+ },
+ "core-js-compat": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.2.1.tgz",
+ "integrity": "sha512-MwPZle5CF9dEaMYdDeWm73ao/IflDH+FjeJCWEADcEgFSE9TLimFKwJsfmkwzI8eC0Aj0mgvMDjeQjrElkz4/A==",
+ "requires": {
+ "browserslist": "^4.6.6",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+ }
+ }
+ },
+ "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="
+ },
+ "cosmiconfig": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+ "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+ "requires": {
+ "import-fresh": "^2.0.0",
+ "is-directory": "^0.3.1",
+ "js-yaml": "^3.13.1",
+ "parse-json": "^4.0.0"
+ }
+ },
+ "create-react-class": {
+ "version": "15.6.3",
+ "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz",
+ "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==",
+ "requires": {
+ "fbjs": "^0.8.9",
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+ "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
+ },
+ "fbjs": {
+ "version": "0.8.17",
+ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
+ "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
+ "requires": {
+ "core-js": "^1.0.0",
+ "isomorphic-fetch": "^2.1.1",
+ "loose-envify": "^1.0.0",
+ "object-assign": "^4.1.0",
+ "promise": "^7.1.1",
+ "setimmediate": "^1.0.5",
+ "ua-parser-js": "^0.7.18"
+ }
+ }
+ }
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "css-in-js-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz",
+ "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==",
+ "requires": {
+ "hyphenate-style-name": "^1.0.2",
+ "isobject": "^3.0.1"
+ }
+ },
+ "debounce": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz",
+ "integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg=="
+ },
+ "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="
+ }
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+ },
+ "decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
+ },
+ "deep-assign": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/deep-assign/-/deep-assign-3.0.0.tgz",
+ "integrity": "sha512-YX2i9XjJ7h5q/aQ/IM9PEwEnDqETAIYbggmdDB3HLTlSgo1CxPsj6pvhPG68rq6SVE0+p+6Ywsm5fTYNrYtBWw==",
+ "requires": {
+ "is-obj": "^1.0.0"
+ }
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "deepmerge": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz",
+ "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA=="
+ },
+ "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==",
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "requires": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "dependencies": {
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
+ }
+ }
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+ },
+ "denodeify": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz",
+ "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE="
+ },
+ "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="
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "dom-walk": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz",
+ "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg="
+ },
+ "dotenv": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-2.0.0.tgz",
+ "integrity": "sha1-vXWcNXqqcDZeAclrewvsCKbg2Uk=",
+ "dev": true
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+ },
+ "electron-to-chromium": {
+ "version": "1.3.241",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.241.tgz",
+ "integrity": "sha512-Gb9E6nWZlbgjDDNe5cAvMJixtn79krNJ70EDpq/M10lkGo7PGtBUe7Y0CYVHsBScRwi6ybCS+YetXAN9ysAHDg=="
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+ },
+ "encoding": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+ "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+ "requires": {
+ "iconv-lite": "~0.4.13"
+ }
+ },
+ "end-of-stream": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
+ "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "envinfo": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-5.12.1.tgz",
+ "integrity": "sha512-pwdo0/G3CIkQ0y6PCXq4RdkvId2elvtPCJMG0konqlrfkWQbf1DWeH9K2b/cvu2YgGvPPTOnonZxXM1gikFu1w=="
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "errorhandler": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz",
+ "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==",
+ "requires": {
+ "accepts": "~1.3.7",
+ "escape-html": "~1.0.3"
+ }
+ },
+ "es-abstract": {
+ "version": "1.14.2",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz",
+ "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==",
+ "requires": {
+ "es-to-primitive": "^1.2.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.0",
+ "is-callable": "^1.1.4",
+ "is-regex": "^1.0.4",
+ "object-inspect": "^1.6.0",
+ "object-keys": "^1.1.1",
+ "string.prototype.trimleft": "^2.0.0",
+ "string.prototype.trimright": "^2.0.0"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
+ "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "es6-error": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
+ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="
+ },
+ "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="
+ },
+ "eslint": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.3.0.tgz",
+ "integrity": "sha512-ZvZTKaqDue+N8Y9g0kp6UPZtS4FSY3qARxBs7p4f0H0iof381XHduqVerFWtK8DPtKmemqbqCFENWSQgPR/Gow==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "ajv": "^6.10.0",
+ "chalk": "^2.1.0",
+ "cross-spawn": "^6.0.5",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "eslint-scope": "^5.0.0",
+ "eslint-utils": "^1.4.2",
+ "eslint-visitor-keys": "^1.1.0",
+ "espree": "^6.1.1",
+ "esquery": "^1.0.1",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^5.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.0.0",
+ "globals": "^11.7.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "inquirer": "^6.4.1",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.3.0",
+ "lodash": "^4.17.14",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.8.2",
+ "progress": "^2.0.0",
+ "regexpp": "^2.0.1",
+ "semver": "^6.1.2",
+ "strip-ansi": "^5.2.0",
+ "strip-json-comments": "^3.0.1",
+ "table": "^5.2.3",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "external-editor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ }
+ },
+ "glob-parent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz",
+ "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "import-fresh": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz",
+ "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "inquirer": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz",
+ "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^3.2.0",
+ "chalk": "^2.4.2",
+ "cli-cursor": "^2.1.0",
+ "cli-width": "^2.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^2.0.0",
+ "lodash": "^4.17.12",
+ "mute-stream": "0.0.7",
+ "run-async": "^2.2.0",
+ "rxjs": "^6.4.0",
+ "string-width": "^2.1.0",
+ "strip-ansi": "^5.1.0",
+ "through": "^2.3.6"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "eslint-config-prettier": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.3.0.tgz",
+ "integrity": "sha512-EWaGjlDAZRzVFveh2Jsglcere2KK5CJBhkNSa1xs3KfMUGdRiT7lG089eqPdvlzWHpAqaekubOsOMu8W8Yk71A==",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^6.0.0"
+ }
+ },
+ "eslint-config-standard": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.0.tgz",
+ "integrity": "sha512-EF6XkrrGVbvv8hL/kYa/m6vnvmUT+K82pJJc4JJVMM6+Qgqh0pnwprSxdduDLB9p/7bIxD+YV5O0wfb8lmcPbA=="
+ },
+ "eslint-plugin-eslint-comments": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.1.2.tgz",
+ "integrity": "sha512-QexaqrNeteFfRTad96W+Vi4Zj1KFbkHHNMMaHZEYcovKav6gdomyGzaxSDSL3GoIyUOo078wRAdYlu1caiauIQ==",
+ "requires": {
+ "escape-string-regexp": "^1.0.5",
+ "ignore": "^5.0.5"
+ },
+ "dependencies": {
+ "ignore": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz",
+ "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A=="
+ }
+ }
+ },
+ "eslint-plugin-flowtype": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-4.3.0.tgz",
+ "integrity": "sha512-elvqoadMHnYqSYN1YXn02DR7SFW8Kc2CLe8na3m2GdQPQhIY+BgCd2quVJ1AbW3aO0zcyE9loVJ7Szy8A/xlMA==",
+ "requires": {
+ "lodash": "^4.17.15"
+ }
+ },
+ "eslint-plugin-jest": {
+ "version": "22.4.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-22.4.1.tgz",
+ "integrity": "sha512-gcLfn6P2PrFAVx3AobaOzlIEevpAEf9chTpFZz7bYfc7pz8XRv7vuKTIE4hxPKZSha6XWKKplDQ0x9Pq8xX2mg=="
+ },
+ "eslint-plugin-prettier": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz",
+ "integrity": "sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==",
+ "dev": true,
+ "requires": {
+ "prettier-linter-helpers": "^1.0.0"
+ }
+ },
+ "eslint-plugin-react": {
+ "version": "7.12.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.12.4.tgz",
+ "integrity": "sha512-1puHJkXJY+oS1t467MjbqjvX53uQ05HXwjqDgdbGBqf5j9eeydI54G3KwiJmWciQ0HTBacIKw2jgwSBSH3yfgQ==",
+ "requires": {
+ "array-includes": "^3.0.3",
+ "doctrine": "^2.1.0",
+ "has": "^1.0.3",
+ "jsx-ast-utils": "^2.0.1",
+ "object.fromentries": "^2.0.0",
+ "prop-types": "^15.6.2",
+ "resolve": "^1.9.0"
+ },
+ "dependencies": {
+ "doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ }
+ }
+ },
+ "eslint-plugin-react-hooks": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz",
+ "integrity": "sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA=="
+ },
+ "eslint-plugin-react-native": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-native/-/eslint-plugin-react-native-3.6.0.tgz",
+ "integrity": "sha512-BEQcHZ06hZSBYWFVuNEq0xuui5VEsWpHDsZGBtfadHfCRqRMUrkYPgdDb3bpc60qShHE83kqIv59uKdinEg91Q==",
+ "requires": {
+ "eslint-plugin-react-native-globals": "^0.1.1"
+ }
+ },
+ "eslint-plugin-react-native-globals": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz",
+ "integrity": "sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g=="
+ },
+ "eslint-scope": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
+ "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz",
+ "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==",
+ "requires": {
+ "eslint-visitor-keys": "^1.0.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
+ "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A=="
+ },
+ "espree": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.1.tgz",
+ "integrity": "sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.0.0",
+ "acorn-jsx": "^5.0.2",
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
+ },
+ "esquery": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.0.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+ "requires": {
+ "estraverse": "^4.1.0"
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+ },
+ "event-target-shim": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-1.1.1.tgz",
+ "integrity": "sha1-qG5e5r2qFgVEddp5fM3fDFVphJE="
+ },
+ "eventemitter3": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
+ "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q=="
+ },
+ "exec-sh": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz",
+ "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==",
+ "requires": {
+ "merge": "^1.2.0"
+ }
+ },
+ "execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "expand-brackets": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
+ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
+ "requires": {
+ "is-posix-bracket": "^0.1.0"
+ }
+ },
+ "expand-range": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
+ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
+ "requires": {
+ "fill-range": "^2.1.0"
+ }
+ },
+ "expo": {
+ "version": "34.0.4",
+ "resolved": "https://registry.npmjs.org/expo/-/expo-34.0.4.tgz",
+ "integrity": "sha512-sZQQoZnN5ASrkSA4qSsk7HPBewHB6b3k9VPZvchT0FBZ1fP5vpmzfIbVOqOLRXHf2VdjNnQVme617TnpPlruJg==",
+ "requires": {
+ "@babel/runtime": "^7.1.2",
+ "@expo/vector-icons": "^10.0.2",
+ "@types/fbemitter": "^2.0.32",
+ "@types/invariant": "^2.2.29",
+ "@types/lodash.zipobject": "^4.1.4",
+ "@types/qs": "^6.5.1",
+ "@types/uuid-js": "^0.7.1",
+ "@unimodules/core": "~3.0.0",
+ "@unimodules/react-native-adapter": "~3.0.0",
+ "babel-preset-expo": "^6.0.0",
+ "cross-spawn": "^6.0.5",
+ "expo-app-loader-provider": "~6.0.0",
+ "expo-asset": "~6.0.0",
+ "expo-constants": "~6.0.0",
+ "expo-file-system": "~6.0.0",
+ "expo-font": "~6.0.1",
+ "expo-keep-awake": "~6.0.0",
+ "expo-linear-gradient": "~6.0.0",
+ "expo-location": "~6.0.0",
+ "expo-permissions": "~6.0.0",
+ "expo-sqlite": "~6.0.0",
+ "expo-web-browser": "~6.0.0",
+ "fbemitter": "^2.1.1",
+ "invariant": "^2.2.2",
+ "lodash": "^4.6.0",
+ "md5-file": "^3.2.3",
+ "nullthrows": "^1.1.0",
+ "pretty-format": "^23.6.0",
+ "prop-types": "^15.6.0",
+ "qs": "^6.5.0",
+ "react-native-branch": "~3.0.1",
+ "react-native-view-shot": "2.6.0",
+ "serialize-error": "^2.1.0",
+ "unimodules-barcode-scanner-interface": "~3.0.0",
+ "unimodules-camera-interface": "~3.0.0",
+ "unimodules-constants-interface": "~3.0.0",
+ "unimodules-face-detector-interface": "~3.0.0",
+ "unimodules-file-system-interface": "~3.0.0",
+ "unimodules-font-interface": "~3.0.0",
+ "unimodules-image-loader-interface": "~3.0.0",
+ "unimodules-permissions-interface": "~3.0.0",
+ "unimodules-sensors-interface": "~3.0.0",
+ "unimodules-task-manager-interface": "~3.0.0",
+ "uuid-js": "^0.7.5"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "expo-file-system": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-6.0.2.tgz",
+ "integrity": "sha512-s+6oQpLhcT7MQp7fcoj1E+zttMr0WX6c0FrddzqB4dUfhIggV+nb35nQMASIiTHAj8VPUanTFliY5rETHRMHRA==",
+ "requires": {
+ "uuid-js": "^0.7.5"
+ }
+ },
+ "pretty-format": {
+ "version": "23.6.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz",
+ "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==",
+ "requires": {
+ "ansi-regex": "^3.0.0",
+ "ansi-styles": "^3.2.0"
+ }
+ }
+ }
+ },
+ "expo-app-loader-provider": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/expo-app-loader-provider/-/expo-app-loader-provider-6.0.0.tgz",
+ "integrity": "sha512-GtpztJVxOz+vVwdLyHskpzVzFWMXZPIFC/zczHZPsTwjS+wXj6n8MVaLxX6GaTyhNEtYjp0VIQUw3b7eP+vO6w=="
+ },
+ "expo-asset": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-6.0.0.tgz",
+ "integrity": "sha512-M0sJphdCQ0mq+7kg6rQmq4rU5hbsL72AZCNrga565JchCLeevJhv6j72erh2viqDAPdvjZpGwc7pwI/dxu1+zg==",
+ "requires": {
+ "blueimp-md5": "^2.10.0",
+ "path-browserify": "^1.0.0",
+ "url-parse": "^1.4.4"
+ }
+ },
+ "expo-constants": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-6.0.0.tgz",
+ "integrity": "sha512-O0yL3Ok0YUEWpAqsWjOdgFD/lMfg8PUGH/nq31CZ1s7cuFUlksD42i5YhIRlb0Pa/btK8X9LpfY3eWhx9eTmbg==",
+ "requires": {
+ "ua-parser-js": "^0.7.19"
+ }
+ },
+ "expo-file-system": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-7.0.0.tgz",
+ "integrity": "sha512-ignf5Vf5cPDYO/4HgUkgnL574wMbCNxyazlOvBgV34rLGJzBbFsn++hqC7njr2VTpIIXh2G9vp1+8g6cvsQdqA==",
+ "requires": {
+ "uuid-js": "^0.7.5"
+ }
+ },
+ "expo-font": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-6.0.1.tgz",
+ "integrity": "sha512-zQwGFTKSrsTWmFzS0l87i6TyqM0YFDK4ui4sSzpbdQsUHXpeG7wfa67i09roLS0xtp85nrR9Vm2bUJp9njV8JQ==",
+ "requires": {
+ "fontfaceobserver": "^2.1.0"
+ }
+ },
+ "expo-image-picker": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-6.0.0.tgz",
+ "integrity": "sha512-7IW4WxDe0xQclGhTnX3DOgTkMvt2kF/MNOPbk2y9fS5k/8am9I8jErjURPxRUGH7+TvYLEWPy63DRAJP7TYyEg=="
+ },
+ "expo-intent-launcher": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/expo-intent-launcher/-/expo-intent-launcher-6.0.0.tgz",
+ "integrity": "sha512-0FZD8NApR05u8WyC4bORzuxKjv0Ojw7EfSPe4igdyo5AFusmarQC8DMbmfWqMZdStWmsSVM0297iziRaNGkPBw=="
+ },
+ "expo-keep-awake": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-6.0.0.tgz",
+ "integrity": "sha512-MAtZknf6FtIC0ipkDS2FVa87al8YBsrpsQ2qMf+H/cI6FOd6aahaggN4x75xGnt5UzozgWfjhGNCi1XCr14rJw=="
+ },
+ "expo-linear-gradient": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-6.0.0.tgz",
+ "integrity": "sha512-TGHSK7MsoU1wZXx9uEivMggAR/KT4wTSE0xBfhB8qsziGXoHZdoT79/tZ3HyWtCG7+JVUEFXfUOBxtOlZOu5tg=="
+ },
+ "expo-localization": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/expo-localization/-/expo-localization-6.0.0.tgz",
+ "integrity": "sha512-ZU0621WDqmoMoko0p+lzGsLIsUTifHhsFqpf0w7S0fC+6djCBD47HFGzbm01sTVrRMS+tOsoVxa8D31ZaBqy5Q==",
+ "requires": {
+ "rtl-detect": "^1.0.2"
+ }
+ },
+ "expo-location": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/expo-location/-/expo-location-6.0.0.tgz",
+ "integrity": "sha512-5uSebmZos0DKJ/xpi+2e9myWVPUWk+fshFedi55wzlGqy2YpTG5MlDcCLlJlamgJ5Tm8+3ECdhbFX3g1pNRDVQ==",
+ "requires": {
+ "invariant": "^2.2.4"
+ }
+ },
+ "expo-permissions": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/expo-permissions/-/expo-permissions-6.0.0.tgz",
+ "integrity": "sha512-O+RdyfGiq7i+5Vi9fE38DgKn436lNWiqhnS5/Z7CC00bmKahhjVMNDbZvNn/nrdRGyaPneJk1Co1s1sexSnv0A=="
+ },
+ "expo-sqlite": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/expo-sqlite/-/expo-sqlite-6.0.0.tgz",
+ "integrity": "sha512-M8heovLeJoq7tb4f7PipDu0dqHSklbI2EqNvDM8XLjSZdSv6CqCMHg5Kvx0L9CLYTchjzktDPClZKjgvtGOVug==",
+ "requires": {
+ "@expo/websql": "^1.0.1",
+ "@types/websql": "^0.0.27",
+ "lodash": "^4.17.11"
+ }
+ },
+ "expo-web-browser": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-6.0.0.tgz",
+ "integrity": "sha512-7XkFPd4PRlVP6FscTnn78c0tY6+yLzb2Eai/ed+l+LB+hZWuhyY3ONzYM7/IKAiPmfhZr4Qs80vIa7iiavti8A=="
+ },
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "external-editor": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
+ "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
+ "requires": {
+ "chardet": "^0.4.0",
+ "iconv-lite": "^0.4.17",
+ "tmp": "^0.0.33"
+ }
+ },
+ "extglob": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
+ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+ "requires": {
+ "is-extglob": "^1.0.0"
+ }
+ },
+ "fancy-log": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
+ "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==",
+ "requires": {
+ "ansi-gray": "^0.1.1",
+ "color-support": "^1.1.3",
+ "parse-node-version": "^1.0.0",
+ "time-stamp": "^1.0.0"
+ }
+ },
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+ "dev": true
+ },
+ "fast-diff": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w=="
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "fb-watchman": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz",
+ "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=",
+ "requires": {
+ "bser": "^2.0.0"
+ }
+ },
+ "fbemitter": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-2.1.1.tgz",
+ "integrity": "sha1-Uj4U/a9SSIBbsC9i78M75wP1GGU=",
+ "requires": {
+ "fbjs": "^0.8.4"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+ "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
+ },
+ "fbjs": {
+ "version": "0.8.17",
+ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
+ "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
+ "requires": {
+ "core-js": "^1.0.0",
+ "isomorphic-fetch": "^2.1.1",
+ "loose-envify": "^1.0.0",
+ "object-assign": "^4.1.0",
+ "promise": "^7.1.1",
+ "setimmediate": "^1.0.5",
+ "ua-parser-js": "^0.7.18"
+ }
+ }
+ }
+ },
+ "fbjs": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-1.0.0.tgz",
+ "integrity": "sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA==",
+ "requires": {
+ "core-js": "^2.4.1",
+ "fbjs-css-vars": "^1.0.0",
+ "isomorphic-fetch": "^2.1.1",
+ "loose-envify": "^1.0.0",
+ "object-assign": "^4.1.0",
+ "promise": "^7.1.1",
+ "setimmediate": "^1.0.5",
+ "ua-parser-js": "^0.7.18"
+ }
+ },
+ "fbjs-css-vars": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz",
+ "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ=="
+ },
+ "fbjs-scripts": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fbjs-scripts/-/fbjs-scripts-1.2.0.tgz",
+ "integrity": "sha512-5krZ8T0Bf8uky0abPoCLrfa7Orxd8UH4Qq8hRUF2RZYNMu+FmEOrBc7Ib3YVONmxTXTlLAvyrrdrVmksDb2OqQ==",
+ "requires": {
+ "@babel/core": "^7.0.0",
+ "ansi-colors": "^1.0.1",
+ "babel-preset-fbjs": "^3.2.0",
+ "core-js": "^2.4.1",
+ "cross-spawn": "^5.1.0",
+ "fancy-log": "^1.3.2",
+ "object-assign": "^4.0.1",
+ "plugin-error": "^0.1.2",
+ "semver": "^5.1.0",
+ "through2": "^2.0.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ }
+ }
+ },
+ "figures": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "file-entry-cache": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+ "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^2.0.1"
+ }
+ },
+ "filename-regex": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
+ "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY="
+ },
+ "fill-range": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
+ "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==",
+ "requires": {
+ "is-number": "^2.1.0",
+ "isobject": "^2.0.0",
+ "randomatic": "^3.0.0",
+ "repeat-element": "^1.1.2",
+ "repeat-string": "^1.5.2"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "find-babel-config": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.2.0.tgz",
+ "integrity": "sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==",
+ "requires": {
+ "json5": "^0.5.1",
+ "path-exists": "^3.0.0"
+ },
+ "dependencies": {
+ "json5": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="
+ }
+ }
+ },
+ "find-cache-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+ "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^2.0.0",
+ "pkg-dir": "^3.0.0"
+ }
+ },
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "flat-cache": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+ "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+ "dev": true,
+ "requires": {
+ "flatted": "^2.0.0",
+ "rimraf": "2.6.3",
+ "write": "1.0.3"
+ },
+ "dependencies": {
+ "rimraf": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ }
+ }
+ },
+ "flatted": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
+ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
+ "dev": true
+ },
+ "fontfaceobserver": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.1.0.tgz",
+ "integrity": "sha512-ReOsO2F66jUa0jmv2nlM/s1MiutJx/srhAe2+TE8dJCMi02ZZOcCTxTCQFr3Yet+uODUtnr4Mewg+tNQ+4V1Ng=="
+ },
+ "for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
+ },
+ "for-own": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
+ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
+ "requires": {
+ "for-in": "^1.0.1"
+ }
+ },
+ "fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "requires": {
+ "map-cache": "^0.2.2"
+ }
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+ },
+ "fs-extra": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz",
+ "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^2.1.0",
+ "klaw": "^1.0.0"
+ },
+ "dependencies": {
+ "jsonfile": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+ "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ }
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "fsevents": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
+ "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
+ "optional": true,
+ "requires": {
+ "nan": "^2.12.1",
+ "node-pre-gyp": "^0.12.0"
+ },
+ "dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "bundled": true,
+ "optional": true
+ },
+ "are-we-there-yet": {
+ "version": "1.1.5",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chownr": {
+ "version": "1.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "bundled": true,
+ "optional": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "bundled": true,
+ "optional": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "bundled": true,
+ "optional": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "detect-libc": {
+ "version": "1.0.3",
+ "bundled": true,
+ "optional": true
+ },
+ "fs-minipass": {
+ "version": "1.2.5",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "gauge": {
+ "version": "2.7.4",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "aproba": "^1.0.3",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.0",
+ "object-assign": "^4.1.0",
+ "signal-exit": "^3.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wide-align": "^1.1.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.3",
+ "bundled": true,
+ "optional": 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"
+ }
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ignore-walk": {
+ "version": "3.0.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "bundled": true,
+ "optional": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "bundled": true,
+ "optional": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "bundled": true,
+ "optional": true
+ },
+ "minipass": {
+ "version": "2.3.5",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "1.2.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "needle": {
+ "version": "2.3.0",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
+ }
+ },
+ "node-pre-gyp": {
+ "version": "0.12.0",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "detect-libc": "^1.0.2",
+ "mkdirp": "^0.5.1",
+ "needle": "^2.2.1",
+ "nopt": "^4.0.1",
+ "npm-packlist": "^1.1.6",
+ "npmlog": "^4.0.2",
+ "rc": "^1.2.7",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^4"
+ }
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "npm-bundled": {
+ "version": "1.0.6",
+ "bundled": true,
+ "optional": true
+ },
+ "npm-packlist": {
+ "version": "1.4.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1"
+ }
+ },
+ "npmlog": {
+ "version": "4.1.2",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "rc": {
+ "version": "1.2.8",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "bundled": true,
+ "optional": true
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "bundled": true,
+ "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"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "bundled": true,
+ "optional": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "bundled": true,
+ "optional": true
+ },
+ "sax": {
+ "version": "1.2.4",
+ "bundled": true,
+ "optional": true
+ },
+ "semver": {
+ "version": "5.7.0",
+ "bundled": true,
+ "optional": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "tar": {
+ "version": "4.4.8",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "chownr": "^1.1.1",
+ "fs-minipass": "^1.2.5",
+ "minipass": "^2.3.4",
+ "minizlib": "^1.1.1",
+ "mkdirp": "^0.5.0",
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.2"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "yallist": {
+ "version": "3.0.3",
+ "bundled": true,
+ "optional": true
+ }
+ }
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "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="
+ },
+ "gauge": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz",
+ "integrity": "sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=",
+ "requires": {
+ "ansi": "^0.3.0",
+ "has-unicode": "^2.0.0",
+ "lodash.pad": "^4.1.0",
+ "lodash.padend": "^4.1.0",
+ "lodash.padstart": "^4.1.0"
+ }
+ },
+ "get-caller-file": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
+ },
+ "get-stdin": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
+ "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
+ "dev": true
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="
+ },
+ "glob": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
+ "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
+ "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"
+ }
+ },
+ "glob-base": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
+ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
+ "requires": {
+ "glob-parent": "^2.0.0",
+ "is-glob": "^2.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
+ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
+ "requires": {
+ "is-glob": "^2.0.0"
+ }
+ },
+ "global": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
+ "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
+ "requires": {
+ "min-document": "^2.19.0",
+ "process": "^0.11.10"
+ }
+ },
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
+ },
+ "graceful-fs": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz",
+ "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q=="
+ },
+ "growly": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
+ "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
+ },
+ "hammerjs": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
+ "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+ },
+ "has-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+ },
+ "has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "requires": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "requires": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "hoist-non-react-statics": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz",
+ "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==",
+ "requires": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "hosted-git-info": {
+ "version": "2.8.4",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz",
+ "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ=="
+ },
+ "http-errors": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
+ "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.1.1",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
+ }
+ },
+ "hyphenate-style-name": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz",
+ "integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ=="
+ },
+ "i18n-js": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/i18n-js/-/i18n-js-3.3.0.tgz",
+ "integrity": "sha512-+m8jh84IIWlFwEJgwrWCkeIwIES9ilJKBOj5qx8ZTLLmlPz7bjKnCdxf254wRf6M4pkQHtgXGT9r9lGk0e9aug=="
+ },
+ "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"
+ }
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "image-size": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz",
+ "integrity": "sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA=="
+ },
+ "immediate": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz",
+ "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw="
+ },
+ "immutable": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
+ "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=",
+ "optional": true
+ },
+ "import-fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+ "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+ "requires": {
+ "caller-path": "^2.0.0",
+ "resolve-from": "^3.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "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=="
+ },
+ "inline-style-prefixer": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-5.1.0.tgz",
+ "integrity": "sha512-giteQHPMrApQOSjNSjteO5ZGSGMRf9gas14fRy2lg2buSc1nRnj6o6xuNds5cMTKrkncyrTu3gJn/yflFMVdmg==",
+ "requires": {
+ "css-in-js-utils": "^2.0.0"
+ }
+ },
+ "inquirer": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
+ "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
+ "requires": {
+ "ansi-escapes": "^3.0.0",
+ "chalk": "^2.0.0",
+ "cli-cursor": "^2.1.0",
+ "cli-width": "^2.0.0",
+ "external-editor": "^2.0.4",
+ "figures": "^2.0.0",
+ "lodash": "^4.3.0",
+ "mute-stream": "0.0.7",
+ "run-async": "^2.2.0",
+ "rx-lite": "^4.0.8",
+ "rx-lite-aggregates": "^4.0.8",
+ "string-width": "^2.1.0",
+ "strip-ansi": "^4.0.0",
+ "through": "^2.3.6"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "invert-kv": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ },
+ "is-callable": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
+ "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA=="
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-date-object": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+ "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY="
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
+ }
+ }
+ },
+ "is-directory": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+ "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
+ },
+ "is-dotfile": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
+ "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE="
+ },
+ "is-equal-shallow": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
+ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=",
+ "requires": {
+ "is-primitive": "^2.0.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
+ },
+ "is-extglob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+ "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA="
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+ },
+ "is-glob": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+ "requires": {
+ "is-extglob": "^1.0.0"
+ }
+ },
+ "is-number": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
+ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "is-posix-bracket": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
+ "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q="
+ },
+ "is-primitive": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
+ "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU="
+ },
+ "is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
+ },
+ "is-regex": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
+ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+ "requires": {
+ "has": "^1.0.1"
+ }
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+ },
+ "is-symbol": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
+ "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
+ "requires": {
+ "has-symbols": "^1.0.0"
+ }
+ },
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
+ },
+ "is-wsl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+ "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0="
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
+ },
+ "isomorphic-fetch": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
+ "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
+ "requires": {
+ "node-fetch": "^1.0.1",
+ "whatwg-fetch": ">=0.10.0"
+ },
+ "dependencies": {
+ "node-fetch": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
+ "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
+ "requires": {
+ "encoding": "^0.1.11",
+ "is-stream": "^1.0.1"
+ }
+ }
+ }
+ },
+ "jest-docblock": {
+ "version": "21.2.0",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz",
+ "integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw=="
+ },
+ "jest-haste-map": {
+ "version": "24.0.0-alpha.6",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.0.0-alpha.6.tgz",
+ "integrity": "sha512-+NO2HMbjvrG8BC39ieLukdpFrcPhhjCJGhpbHodHNZygH1Tt06WrlNYGpZtWKx/zpf533tCtMQXO/q59JenjNw==",
+ "requires": {
+ "fb-watchman": "^2.0.0",
+ "graceful-fs": "^4.1.11",
+ "invariant": "^2.2.4",
+ "jest-serializer": "^24.0.0-alpha.6",
+ "jest-worker": "^24.0.0-alpha.6",
+ "micromatch": "^2.3.11",
+ "sane": "^3.0.0"
+ }
+ },
+ "jest-serializer": {
+ "version": "24.9.0",
+ "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz",
+ "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ=="
+ },
+ "jest-worker": {
+ "version": "24.0.0-alpha.6",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.0.0-alpha.6.tgz",
+ "integrity": "sha512-iXtH7MR9bjWlNnlnRBcrBRrb4cSVxML96La5vsnmBvDI+mJnkP5uEt6Fgpo5Y8f3z9y2Rd7wuPnKRxqQsiU/dA==",
+ "requires": {
+ "merge-stream": "^1.0.1"
+ }
+ },
+ "js-levenshtein": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
+ "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g=="
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
+ },
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
+ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
+ "requires": {
+ "jsonify": "~0.0.0"
+ }
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "json5": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz",
+ "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==",
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "jsonify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
+ },
+ "jsx-ast-utils": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz",
+ "integrity": "sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ==",
+ "requires": {
+ "array-includes": "^3.0.3",
+ "object.assign": "^4.1.0"
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ },
+ "klaw": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
+ "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
+ "requires": {
+ "graceful-fs": "^4.1.9"
+ }
+ },
+ "lcid": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+ "requires": {
+ "invert-kv": "^1.0.0"
+ }
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "load-json-file": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "strip-bom": "^3.0.0"
+ },
+ "dependencies": {
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ }
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
+ },
+ "lodash-es": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz",
+ "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ=="
+ },
+ "lodash.get": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
+ },
+ "lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+ },
+ "lodash.isfunction": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
+ "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw=="
+ },
+ "lodash.isobject": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz",
+ "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0="
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+ },
+ "lodash.pad": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz",
+ "integrity": "sha1-QzCUmoM6fI2iLMIPaibE1Z3runA="
+ },
+ "lodash.padend": {
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
+ "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4="
+ },
+ "lodash.padstart": {
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz",
+ "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs="
+ },
+ "lodash.throttle": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+ "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+ },
+ "lodash.unescape": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
+ "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw="
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "requires": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="
+ }
+ }
+ },
+ "makeerror": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz",
+ "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=",
+ "requires": {
+ "tmpl": "1.0.x"
+ }
+ },
+ "map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8="
+ },
+ "map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "requires": {
+ "object-visit": "^1.0.0"
+ }
+ },
+ "math-random": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz",
+ "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A=="
+ },
+ "md5-file": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-3.2.3.tgz",
+ "integrity": "sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw==",
+ "requires": {
+ "buffer-alloc": "^1.1.0"
+ }
+ },
+ "mem": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
+ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ },
+ "merge": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz",
+ "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ=="
+ },
+ "merge-stream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz",
+ "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=",
+ "requires": {
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "metro": {
+ "version": "0.51.1",
+ "resolved": "https://registry.npmjs.org/metro/-/metro-0.51.1.tgz",
+ "integrity": "sha512-nM0dqn8LQlMjhChl2fzTUq2EWiUebZM7nkesD9vQe47W10bj/tbRLPiIIAxht6SRDbPd/hRA+t39PxLhPSKEKg==",
+ "requires": {
+ "@babel/core": "^7.0.0",
+ "@babel/generator": "^7.0.0",
+ "@babel/parser": "^7.0.0",
+ "@babel/plugin-external-helpers": "^7.0.0",
+ "@babel/template": "^7.0.0",
+ "@babel/traverse": "^7.0.0",
+ "@babel/types": "^7.0.0",
+ "absolute-path": "^0.0.0",
+ "async": "^2.4.0",
+ "babel-preset-fbjs": "^3.0.1",
+ "buffer-crc32": "^0.2.13",
+ "chalk": "^2.4.1",
+ "concat-stream": "^1.6.0",
+ "connect": "^3.6.5",
+ "debug": "^2.2.0",
+ "denodeify": "^1.2.1",
+ "eventemitter3": "^3.0.0",
+ "fbjs": "^1.0.0",
+ "fs-extra": "^1.0.0",
+ "graceful-fs": "^4.1.3",
+ "image-size": "^0.6.0",
+ "invariant": "^2.2.4",
+ "jest-haste-map": "24.0.0-alpha.6",
+ "jest-worker": "24.0.0-alpha.6",
+ "json-stable-stringify": "^1.0.1",
+ "lodash.throttle": "^4.1.1",
+ "merge-stream": "^1.0.1",
+ "metro-babel-transformer": "0.51.1",
+ "metro-cache": "0.51.1",
+ "metro-config": "0.51.1",
+ "metro-core": "0.51.1",
+ "metro-minify-uglify": "0.51.1",
+ "metro-react-native-babel-preset": "0.51.1",
+ "metro-resolver": "0.51.1",
+ "metro-source-map": "0.51.1",
+ "mime-types": "2.1.11",
+ "mkdirp": "^0.5.1",
+ "node-fetch": "^2.2.0",
+ "nullthrows": "^1.1.0",
+ "react-transform-hmr": "^1.0.4",
+ "resolve": "^1.5.0",
+ "rimraf": "^2.5.4",
+ "serialize-error": "^2.1.0",
+ "source-map": "^0.5.6",
+ "temp": "0.8.3",
+ "throat": "^4.1.0",
+ "wordwrap": "^1.0.0",
+ "write-file-atomic": "^1.2.0",
+ "ws": "^1.1.5",
+ "xpipe": "^1.0.5",
+ "yargs": "^9.0.0"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.23.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz",
+ "integrity": "sha1-oxtAcK2uon1zLqMzdApk0OyaZlk="
+ },
+ "mime-types": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz",
+ "integrity": "sha1-wlnEcb2oCKhdbNGTtDCl+uRHOzw=",
+ "requires": {
+ "mime-db": "~1.23.0"
+ }
+ }
+ }
+ },
+ "metro-babel-register": {
+ "version": "0.51.0",
+ "resolved": "https://registry.npmjs.org/metro-babel-register/-/metro-babel-register-0.51.0.tgz",
+ "integrity": "sha512-rhdvHFOZ7/ub019A3+aYs8YeLydb02/FAMsKr2Nz2Jlf6VUxWrMnrcT0NYX16F9TGdi2ulRlJ9dwvUmdhkk+Bw==",
+ "requires": {
+ "@babel/core": "^7.0.0",
+ "@babel/plugin-proposal-class-properties": "^7.0.0",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
+ "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.0.0",
+ "@babel/plugin-proposal-optional-chaining": "^7.0.0",
+ "@babel/plugin-transform-async-to-generator": "^7.0.0",
+ "@babel/plugin-transform-flow-strip-types": "^7.0.0",
+ "@babel/plugin-transform-modules-commonjs": "^7.0.0",
+ "@babel/register": "^7.0.0",
+ "core-js": "^2.2.2",
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "metro-babel-transformer": {
+ "version": "0.51.1",
+ "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.51.1.tgz",
+ "integrity": "sha512-+tOnZZzOzufB86ASdfimUEGB1jBKsdsVpPdjNJZkueTFyvYlGqWDQKHM1w9bwKMeM/czPQ48Y6m8Bou6le0X4w==",
+ "requires": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "metro-babel7-plugin-react-transform": {
+ "version": "0.51.1",
+ "resolved": "https://registry.npmjs.org/metro-babel7-plugin-react-transform/-/metro-babel7-plugin-react-transform-0.51.1.tgz",
+ "integrity": "sha512-wzn4X9KgmAMZ7Bi6v9KxA7dw+AHGL0RODPxU5NDJ3A6d0yERvzfZ3qkzWhz8jbFkVBK12cu5DTho3HBazKQDOw==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0"
+ }
+ },
+ "metro-cache": {
+ "version": "0.51.1",
+ "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.51.1.tgz",
+ "integrity": "sha512-0m1+aicsw77LVAehNuTxDpE1c/7Xv/ajRD+UL/lFCWUxnrjSbxVtIKr8l5DxEY11082c1axVRuaV9e436W+eXg==",
+ "requires": {
+ "jest-serializer": "24.0.0-alpha.6",
+ "metro-core": "0.51.1",
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.4"
+ },
+ "dependencies": {
+ "jest-serializer": {
+ "version": "24.0.0-alpha.6",
+ "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.0.0-alpha.6.tgz",
+ "integrity": "sha512-IPA5T6/GhlE6dedSk7Cd7YfuORnYjN0VD5iJVFn1Q81RJjpj++Hen5kJbKcg547vXsQ1TddV15qOA/zeIfOCLw=="
+ }
+ }
+ },
+ "metro-config": {
+ "version": "0.51.1",
+ "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.51.1.tgz",
+ "integrity": "sha512-WCNd0tTI9gb/ubgTqK1+ljZL4b3hsXVinsOAtep4nHiVb6DSDdbO2yXDD2rpYx3NE6hDRMFS9HHg6G0139pAqQ==",
+ "requires": {
+ "cosmiconfig": "^5.0.5",
+ "metro": "0.51.1",
+ "metro-cache": "0.51.1",
+ "metro-core": "0.51.1",
+ "pretty-format": "24.0.0-alpha.6"
+ }
+ },
+ "metro-core": {
+ "version": "0.51.1",
+ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.51.1.tgz",
+ "integrity": "sha512-sG1yACjdFqmIzZN50HqLTKUMp1oy0AehHhmIuYeIllo1DjX6Y2o3UAT3rGP8U+SAqJGXf/OWzl6VNyRPGDENfA==",
+ "requires": {
+ "jest-haste-map": "24.0.0-alpha.6",
+ "lodash.throttle": "^4.1.1",
+ "metro-resolver": "0.51.1",
+ "wordwrap": "^1.0.0"
+ }
+ },
+ "metro-memory-fs": {
+ "version": "0.51.1",
+ "resolved": "https://registry.npmjs.org/metro-memory-fs/-/metro-memory-fs-0.51.1.tgz",
+ "integrity": "sha512-dXVUpLPLwfQcYHd1HlqHGVzBsiwvUdT92TDSbdc10152TP+iynHBqLDWbxt0MAtd6c/QXwOuGZZ1IcX3+lv5iw=="
+ },
+ "metro-minify-uglify": {
+ "version": "0.51.1",
+ "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.51.1.tgz",
+ "integrity": "sha512-HAqd/rFrQ6mnbqVAszDXIKTg2rqHlY9Fm8DReakgbkAeyMbF2mH3kEgtesPmTrhajdFk81UZcNSm6wxj1JMgVg==",
+ "requires": {
+ "uglify-es": "^3.1.9"
+ }
+ },
+ "metro-react-native-babel-preset": {
+ "version": "0.51.1",
+ "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.51.1.tgz",
+ "integrity": "sha512-e9tsYDFhU70gar0jQWcZXRPJVCv4k7tEs6Pm74wXO2OO/T1MEumbvniDIGwGG8bG8RUnYdHhjcaiub2Vc5BRWw==",
+ "requires": {
+ "@babel/plugin-proposal-class-properties": "^7.0.0",
+ "@babel/plugin-proposal-export-default-from": "^7.0.0",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
+ "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.0.0",
+ "@babel/plugin-proposal-optional-chaining": "^7.0.0",
+ "@babel/plugin-syntax-dynamic-import": "^7.0.0",
+ "@babel/plugin-syntax-export-default-from": "^7.0.0",
+ "@babel/plugin-transform-arrow-functions": "^7.0.0",
+ "@babel/plugin-transform-block-scoping": "^7.0.0",
+ "@babel/plugin-transform-classes": "^7.0.0",
+ "@babel/plugin-transform-computed-properties": "^7.0.0",
+ "@babel/plugin-transform-destructuring": "^7.0.0",
+ "@babel/plugin-transform-exponentiation-operator": "^7.0.0",
+ "@babel/plugin-transform-flow-strip-types": "^7.0.0",
+ "@babel/plugin-transform-for-of": "^7.0.0",
+ "@babel/plugin-transform-function-name": "^7.0.0",
+ "@babel/plugin-transform-literals": "^7.0.0",
+ "@babel/plugin-transform-modules-commonjs": "^7.0.0",
+ "@babel/plugin-transform-object-assign": "^7.0.0",
+ "@babel/plugin-transform-parameters": "^7.0.0",
+ "@babel/plugin-transform-react-display-name": "^7.0.0",
+ "@babel/plugin-transform-react-jsx": "^7.0.0",
+ "@babel/plugin-transform-react-jsx-source": "^7.0.0",
+ "@babel/plugin-transform-regenerator": "^7.0.0",
+ "@babel/plugin-transform-runtime": "^7.0.0",
+ "@babel/plugin-transform-shorthand-properties": "^7.0.0",
+ "@babel/plugin-transform-spread": "^7.0.0",
+ "@babel/plugin-transform-sticky-regex": "^7.0.0",
+ "@babel/plugin-transform-template-literals": "^7.0.0",
+ "@babel/plugin-transform-typescript": "^7.0.0",
+ "@babel/plugin-transform-unicode-regex": "^7.0.0",
+ "@babel/template": "^7.0.0",
+ "metro-babel7-plugin-react-transform": "0.51.1",
+ "react-transform-hmr": "^1.0.4"
+ }
+ },
+ "metro-react-native-babel-transformer": {
+ "version": "0.51.0",
+ "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.51.0.tgz",
+ "integrity": "sha512-VFnqtE0qrVmU1HV9B04o53+NZHvDwR+CWCoEx4+7vCqJ9Tvas741biqCjah9xtifoKdElQELk6x0soOAWCDFJA==",
+ "requires": {
+ "@babel/core": "^7.0.0",
+ "babel-preset-fbjs": "^3.0.1",
+ "metro-babel-transformer": "0.51.0",
+ "metro-react-native-babel-preset": "0.51.0"
+ },
+ "dependencies": {
+ "metro-babel-transformer": {
+ "version": "0.51.0",
+ "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.51.0.tgz",
+ "integrity": "sha512-M7KEY/hjD3E8tJEliWgI0VOSaJtqaznC0ItM6FiMrhoGDqqa1BvGofl+EPcKqjBSOV1UgExua/T1VOIWbjwQsw==",
+ "requires": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "metro-babel7-plugin-react-transform": {
+ "version": "0.51.0",
+ "resolved": "https://registry.npmjs.org/metro-babel7-plugin-react-transform/-/metro-babel7-plugin-react-transform-0.51.0.tgz",
+ "integrity": "sha512-dZ95kXcE2FJMoRsYhxr7YLCbOlHWKwe0bOpihRhfImDTgFfuKIzU4ROQwMUbE0NCbzB+ATFsa2FZ3pHDJ5GI0w==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0"
+ }
+ },
+ "metro-react-native-babel-preset": {
+ "version": "0.51.0",
+ "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.51.0.tgz",
+ "integrity": "sha512-Y/aPeLl4RzY8IEAneOyDcpdjto/8yjIuX9eUWRngjSqdHYhGQtqiSBpfTpo0BvXpwNRLwCLHyXo58gNpckTJFw==",
+ "requires": {
+ "@babel/plugin-proposal-class-properties": "^7.0.0",
+ "@babel/plugin-proposal-export-default-from": "^7.0.0",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
+ "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.0.0",
+ "@babel/plugin-proposal-optional-chaining": "^7.0.0",
+ "@babel/plugin-syntax-dynamic-import": "^7.0.0",
+ "@babel/plugin-syntax-export-default-from": "^7.0.0",
+ "@babel/plugin-transform-arrow-functions": "^7.0.0",
+ "@babel/plugin-transform-block-scoping": "^7.0.0",
+ "@babel/plugin-transform-classes": "^7.0.0",
+ "@babel/plugin-transform-computed-properties": "^7.0.0",
+ "@babel/plugin-transform-destructuring": "^7.0.0",
+ "@babel/plugin-transform-exponentiation-operator": "^7.0.0",
+ "@babel/plugin-transform-flow-strip-types": "^7.0.0",
+ "@babel/plugin-transform-for-of": "^7.0.0",
+ "@babel/plugin-transform-function-name": "^7.0.0",
+ "@babel/plugin-transform-literals": "^7.0.0",
+ "@babel/plugin-transform-modules-commonjs": "^7.0.0",
+ "@babel/plugin-transform-object-assign": "^7.0.0",
+ "@babel/plugin-transform-parameters": "^7.0.0",
+ "@babel/plugin-transform-react-display-name": "^7.0.0",
+ "@babel/plugin-transform-react-jsx": "^7.0.0",
+ "@babel/plugin-transform-react-jsx-source": "^7.0.0",
+ "@babel/plugin-transform-regenerator": "^7.0.0",
+ "@babel/plugin-transform-runtime": "^7.0.0",
+ "@babel/plugin-transform-shorthand-properties": "^7.0.0",
+ "@babel/plugin-transform-spread": "^7.0.0",
+ "@babel/plugin-transform-sticky-regex": "^7.0.0",
+ "@babel/plugin-transform-template-literals": "^7.0.0",
+ "@babel/plugin-transform-typescript": "^7.0.0",
+ "@babel/plugin-transform-unicode-regex": "^7.0.0",
+ "@babel/template": "^7.0.0",
+ "metro-babel7-plugin-react-transform": "0.51.0",
+ "react-transform-hmr": "^1.0.4"
+ }
+ }
+ }
+ },
+ "metro-resolver": {
+ "version": "0.51.1",
+ "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.51.1.tgz",
+ "integrity": "sha512-zmWbD/287NDA/jLPuPV0hne/YMMSG0dljzu21TYMg2lXRLur/zROJHHhyepZvuBHgInXBi4Vhr2wvuSnY39SuA==",
+ "requires": {
+ "absolute-path": "^0.0.0"
+ }
+ },
+ "metro-source-map": {
+ "version": "0.51.1",
+ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.51.1.tgz",
+ "integrity": "sha512-JyrE+RV4YumrboHPHTGsUUGERjQ681ImRLrSYDGcmNv4tfpk9nvAK26UAas4IvBYFCC9oW90m0udt3kaQGv59Q==",
+ "requires": {
+ "source-map": "^0.5.6"
+ }
+ },
+ "micromatch": {
+ "version": "2.3.11",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
+ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
+ "requires": {
+ "arr-diff": "^2.0.0",
+ "array-unique": "^0.2.1",
+ "braces": "^1.8.2",
+ "expand-brackets": "^0.1.4",
+ "extglob": "^0.3.1",
+ "filename-regex": "^2.0.0",
+ "is-extglob": "^1.0.0",
+ "is-glob": "^2.0.1",
+ "kind-of": "^3.0.2",
+ "normalize-path": "^2.0.1",
+ "object.omit": "^2.0.0",
+ "parse-glob": "^3.0.4",
+ "regex-cache": "^0.4.2"
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+ },
+ "mime-db": {
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
+ "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
+ },
+ "mime-types": {
+ "version": "2.1.24",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
+ "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
+ "requires": {
+ "mime-db": "1.40.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
+ },
+ "min-document": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
+ "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
+ "requires": {
+ "dom-walk": "^0.1.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ },
+ "mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "requires": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "requires": {
+ "minimist": "0.0.8"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ }
+ }
+ },
+ "moment": {
+ "version": "2.24.0",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
+ "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
+ },
+ "morgan": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
+ "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
+ "requires": {
+ "basic-auth": "~2.0.0",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "on-headers": "~1.0.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "mute-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
+ },
+ "nan": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
+ "optional": true
+ },
+ "nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
+ }
+ }
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "negotiator": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
+ },
+ "node-fetch": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
+ "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
+ },
+ "node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs="
+ },
+ "node-modules-regexp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
+ "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA="
+ },
+ "node-notifier": {
+ "version": "5.4.3",
+ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz",
+ "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==",
+ "requires": {
+ "growly": "^1.3.0",
+ "is-wsl": "^1.1.0",
+ "semver": "^5.5.0",
+ "shellwords": "^0.1.1",
+ "which": "^1.3.0"
+ }
+ },
+ "node-releases": {
+ "version": "1.1.28",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.28.tgz",
+ "integrity": "sha512-AQw4emh6iSXnCpDiFe0phYcThiccmkNWMZnFZ+lDJjAP8J0m2fVd59duvUUyuTirQOhIAajTFkzG6FHCLBO59g==",
+ "requires": {
+ "semver": "^5.3.0"
+ }
+ },
+ "noop-fn": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/noop-fn/-/noop-fn-1.0.0.tgz",
+ "integrity": "sha1-XzPUfxPSFQ35PgywNmmemC94/78="
+ },
+ "normalize-css-color": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/normalize-css-color/-/normalize-css-color-1.0.2.tgz",
+ "integrity": "sha1-Apkel8zOxmI/5XOvu/Deah8+n40="
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ },
+ "npm-run-path": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+ "requires": {
+ "path-key": "^2.0.0"
+ }
+ },
+ "npmlog": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-2.0.4.tgz",
+ "integrity": "sha1-mLUlMPJRTKkNCexbIsiEZyI3VpI=",
+ "requires": {
+ "ansi": "~0.3.1",
+ "are-we-there-yet": "~1.1.2",
+ "gauge": "~1.2.5"
+ }
+ },
+ "nullthrows": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz",
+ "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw=="
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+ },
+ "object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+ "requires": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "object-inspect": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz",
+ "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ=="
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
+ },
+ "object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "requires": {
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "requires": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "object-keys": "^1.0.11"
+ }
+ },
+ "object.fromentries": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz",
+ "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==",
+ "requires": {
+ "define-properties": "^1.1.2",
+ "es-abstract": "^1.11.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.1"
+ }
+ },
+ "object.omit": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
+ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
+ "requires": {
+ "for-own": "^0.1.4",
+ "is-extendable": "^0.1.1"
+ }
+ },
+ "object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "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"
+ }
+ },
+ "on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ },
+ "opencollective-postinstall": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz",
+ "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw=="
+ },
+ "opn": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-3.0.3.tgz",
+ "integrity": "sha1-ttmec5n3jWXDuq/+8fsojpuFJDo=",
+ "requires": {
+ "object-assign": "^4.0.1"
+ }
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
+ },
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
+ }
+ }
+ },
+ "optionator": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.4",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "wordwrap": "~1.0.0"
+ }
+ },
+ "options": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
+ "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8="
+ },
+ "os-locale": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
+ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
+ "requires": {
+ "execa": "^0.7.0",
+ "lcid": "^1.0.0",
+ "mem": "^1.1.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "execa": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
+ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+ "requires": {
+ "cross-spawn": "^5.0.1",
+ "get-stream": "^3.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
+ }
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+ },
+ "p-finally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ },
+ "dependencies": {
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ }
+ }
+ },
+ "parse-glob": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
+ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
+ "requires": {
+ "glob-base": "^0.3.0",
+ "is-dotfile": "^1.0.0",
+ "is-extglob": "^1.0.0",
+ "is-glob": "^2.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ },
+ "parse-node-version": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
+ "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA=="
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+ },
+ "pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="
+ },
+ "path-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.0.tgz",
+ "integrity": "sha512-Hkavx/nY4/plImrZPHRk2CL9vpOymZLgEbMNX1U0bjcBL7QN9wODxyx0yaMZURSQaUtSEvDrfAvxa9oPb0at9g=="
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+ },
+ "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="
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
+ },
+ "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=="
+ },
+ "path-to-regexp": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
+ "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
+ "requires": {
+ "isarray": "0.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ }
+ }
+ },
+ "path-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "requires": {
+ "pify": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ },
+ "pirates": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz",
+ "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==",
+ "requires": {
+ "node-modules-regexp": "^1.0.0"
+ }
+ },
+ "pkg-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+ "requires": {
+ "find-up": "^3.0.0"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
+ "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "requires": {
+ "p-limit": "^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=="
+ }
+ }
+ },
+ "pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
+ "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
+ "requires": {
+ "find-up": "^2.1.0"
+ }
+ },
+ "plist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.1.tgz",
+ "integrity": "sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==",
+ "requires": {
+ "base64-js": "^1.2.3",
+ "xmlbuilder": "^9.0.7",
+ "xmldom": "0.1.x"
+ }
+ },
+ "plugin-error": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
+ "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=",
+ "requires": {
+ "ansi-cyan": "^0.1.1",
+ "ansi-red": "^0.1.1",
+ "arr-diff": "^1.0.1",
+ "arr-union": "^2.0.1",
+ "extend-shallow": "^1.1.2"
+ },
+ "dependencies": {
+ "arr-diff": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
+ "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=",
+ "requires": {
+ "arr-flatten": "^1.0.1",
+ "array-slice": "^0.2.3"
+ }
+ },
+ "arr-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
+ "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0="
+ },
+ "extend-shallow": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
+ "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=",
+ "requires": {
+ "kind-of": "^1.1.0"
+ }
+ },
+ "kind-of": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
+ "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ="
+ }
+ }
+ },
+ "posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
+ },
+ "pouchdb-collections": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-1.0.1.tgz",
+ "integrity": "sha1-/mOhfal3YRq+98uAJssalVP9g1k="
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "preserve": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
+ "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks="
+ },
+ "prettier": {
+ "version": "1.18.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz",
+ "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==",
+ "dev": true
+ },
+ "prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "requires": {
+ "fast-diff": "^1.1.2"
+ }
+ },
+ "pretty-format": {
+ "version": "24.0.0-alpha.6",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0-alpha.6.tgz",
+ "integrity": "sha512-zG2m6YJeuzwBFqb5EIdmwYVf30sap+iMRuYNPytOccEXZMAJbPIFGKVJ/U0WjQegmnQbRo9CI7j6j3HtDaifiA==",
+ "requires": {
+ "ansi-regex": "^4.0.0",
+ "ansi-styles": "^3.2.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
+ }
+ }
+ },
+ "private": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
+ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg=="
+ },
+ "process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
+ },
+ "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=="
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "requires": {
+ "asap": "~2.0.3"
+ }
+ },
+ "prop-types": {
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
+ }
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.8.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.8.0.tgz",
+ "integrity": "sha512-tPSkj8y92PfZVbinY1n84i1Qdx75lZjMQYx9WZhnkofyxzw2r7Ho39G3/aEvSUdebxpnnM4LZJCtvE/Aq3+s9w=="
+ },
+ "query-string": {
+ "version": "6.8.3",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.8.3.tgz",
+ "integrity": "sha512-llcxWccnyaWlODe7A9hRjkvdCKamEKTh+wH8ITdTc3OhchaqUZteiSCX/2ablWHVrkVIe04dntnaZJ7BdyW0lQ==",
+ "requires": {
+ "decode-uri-component": "^0.2.0",
+ "split-on-first": "^1.0.0",
+ "strict-uri-encode": "^2.0.0"
+ }
+ },
+ "querystringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz",
+ "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA=="
+ },
+ "randomatic": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz",
+ "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==",
+ "requires": {
+ "is-number": "^4.0.0",
+ "kind-of": "^6.0.0",
+ "math-random": "^1.0.1"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+ "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ=="
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
+ }
+ }
+ },
+ "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=="
+ },
+ "react": {
+ "version": "16.8.3",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.8.3.tgz",
+ "integrity": "sha512-3UoSIsEq8yTJuSu0luO1QQWYbgGEILm+eJl2QN/VLDi7hL+EN18M3q3oVZwmVzzBJ3DkM7RMdRwBmZZ+b4IzSA==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.6.2",
+ "scheduler": "^0.13.3"
+ }
+ },
+ "react-clone-referenced-element": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/react-clone-referenced-element/-/react-clone-referenced-element-1.1.0.tgz",
+ "integrity": "sha512-FKOsfKbBkPxYE8576EM6uAfHC4rnMpLyH6/TJUL4WcHUEB3EUn8AxPjnnV/IiwSSzsClvHYK+sDELKN/EJ0WYg=="
+ },
+ "react-deep-force-update": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/react-deep-force-update/-/react-deep-force-update-1.1.2.tgz",
+ "integrity": "sha512-WUSQJ4P/wWcusaH+zZmbECOk7H5N2pOIl0vzheeornkIMhu+qrNdGFm0bDZLCb0hSF0jf/kH1SgkNGfBdTc4wA=="
+ },
+ "react-devtools-core": {
+ "version": "3.6.3",
+ "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-3.6.3.tgz",
+ "integrity": "sha512-+P+eFy/yo8Z/UH9J0DqHZuUM5+RI2wl249TNvMx3J2jpUomLQa4Zxl56GEotGfw3PIP1eI+hVf1s53FlUONStQ==",
+ "requires": {
+ "shell-quote": "^1.6.1",
+ "ws": "^3.3.1"
+ },
+ "dependencies": {
+ "ultron": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
+ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
+ },
+ "ws": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
+ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+ "requires": {
+ "async-limiter": "~1.0.0",
+ "safe-buffer": "~5.1.0",
+ "ultron": "~1.1.0"
+ }
+ }
+ }
+ },
+ "react-dom": {
+ "version": "16.9.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.9.0.tgz",
+ "integrity": "sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.6.2",
+ "scheduler": "^0.15.0"
+ },
+ "dependencies": {
+ "scheduler": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz",
+ "integrity": "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ }
+ }
+ }
+ },
+ "react-is": {
+ "version": "16.9.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz",
+ "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw=="
+ },
+ "react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "react-native": {
+ "version": "https://github.com/expo/react-native/archive/sdk-34.0.0.tar.gz",
+ "integrity": "sha512-A9OfDlTZN9haCCSeX+jq/5HU1b7/0EQNKKJIz0dfYKLCYj1lZAJhoQb1xtGoRaTYKk/fcRwxe/amIYms9mFjAg==",
+ "requires": {
+ "@babel/runtime": "^7.0.0",
+ "@react-native-community/cli": "^1.2.1",
+ "absolute-path": "^0.0.0",
+ "art": "^0.10.0",
+ "base64-js": "^1.1.2",
+ "chalk": "^2.4.1",
+ "commander": "^2.9.0",
+ "compression": "^1.7.1",
+ "connect": "^3.6.5",
+ "create-react-class": "^15.6.3",
+ "debug": "^2.2.0",
+ "denodeify": "^1.2.1",
+ "errorhandler": "^1.5.0",
+ "escape-string-regexp": "^1.0.5",
+ "event-target-shim": "^1.0.5",
+ "fbjs": "^1.0.0",
+ "fbjs-scripts": "^1.0.0",
+ "fs-extra": "^1.0.0",
+ "glob": "^7.1.1",
+ "graceful-fs": "^4.1.3",
+ "inquirer": "^3.0.6",
+ "invariant": "^2.2.4",
+ "lodash": "^4.17.5",
+ "metro-babel-register": "0.51.0",
+ "metro-react-native-babel-transformer": "0.51.0",
+ "mime": "^1.3.4",
+ "minimist": "^1.2.0",
+ "mkdirp": "^0.5.1",
+ "morgan": "^1.9.0",
+ "node-fetch": "^2.2.0",
+ "node-notifier": "^5.2.1",
+ "npmlog": "^2.0.4",
+ "nullthrows": "^1.1.0",
+ "opn": "^3.0.2",
+ "optimist": "^0.6.1",
+ "plist": "^3.0.0",
+ "pretty-format": "24.0.0-alpha.6",
+ "promise": "^7.1.1",
+ "prop-types": "^15.5.8",
+ "react-clone-referenced-element": "^1.0.1",
+ "react-devtools-core": "^3.6.0",
+ "regenerator-runtime": "^0.11.0",
+ "rimraf": "^2.5.4",
+ "semver": "^5.0.3",
+ "serve-static": "^1.13.1",
+ "shell-quote": "1.6.1",
+ "stacktrace-parser": "0.1.4",
+ "ws": "^1.1.5",
+ "xmldoc": "^0.4.0",
+ "yargs": "^9.0.0"
+ },
+ "dependencies": {
+ "@react-native-community/cli": {
+ "version": "1.11.2",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-1.11.2.tgz",
+ "integrity": "sha512-5NuYd30f5PCTrGUbZLnusZKv5nfTWvTDTRa/3Q4vwdMnUQrhm9sZXWGQ5CnFoQ7cE58EAqhj6/ShXeJF3DZ9uQ==",
+ "requires": {
+ "chalk": "^1.1.1",
+ "commander": "^2.19.0",
+ "compression": "^1.7.1",
+ "connect": "^3.6.5",
+ "denodeify": "^1.2.1",
+ "envinfo": "^5.7.0",
+ "errorhandler": "^1.5.0",
+ "escape-string-regexp": "^1.0.5",
+ "execa": "^1.0.0",
+ "fs-extra": "^7.0.1",
+ "glob": "^7.1.1",
+ "graceful-fs": "^4.1.3",
+ "inquirer": "^3.0.6",
+ "lodash": "^4.17.5",
+ "metro": "^0.51.0",
+ "metro-config": "^0.51.0",
+ "metro-core": "^0.51.0",
+ "metro-memory-fs": "^0.51.0",
+ "metro-react-native-babel-transformer": "^0.51.0",
+ "mime": "^1.3.4",
+ "minimist": "^1.2.0",
+ "mkdirp": "^0.5.1",
+ "morgan": "^1.9.0",
+ "node-fetch": "^2.2.0",
+ "node-notifier": "^5.2.1",
+ "opn": "^3.0.2",
+ "plist": "^3.0.0",
+ "semver": "^5.0.3",
+ "serve-static": "^1.13.1",
+ "shell-quote": "1.6.1",
+ "slash": "^2.0.0",
+ "ws": "^1.1.0",
+ "xcode": "^2.0.0",
+ "xmldoc": "^0.4.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ }
+ }
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
+ }
+ }
+ },
+ "react-native-actionsheet": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/react-native-actionsheet/-/react-native-actionsheet-2.4.2.tgz",
+ "integrity": "sha512-DBoWIvVwuWXuptF4t46pBqkFxaUxS+rsIdHiA05t0n4BdTIDV2R4s9bLEUVOGzb94D7VxIamsXZPA/3mmw+SXg=="
+ },
+ "react-native-animatable": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.3.2.tgz",
+ "integrity": "sha512-rmah3KQ63ft8DxkzFUwJSuZeq+oSYwldoGF4DTOR5WM2WR5wiWLgBAtrAHlI3Di3by323uOR21s+MlqPcHz2Kw==",
+ "requires": {
+ "prop-types": "^15.5.10"
+ }
+ },
+ "react-native-branch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/react-native-branch/-/react-native-branch-3.0.1.tgz",
+ "integrity": "sha512-vbcYxPZlpF5f39GAEUF8kuGQqCNeD3E6zEdvtOq8oCGZunHXlWlKgAS6dgBKCvsHvXgHuMtpvs39VgOp8DaKig=="
+ },
+ "react-native-dotenv": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/react-native-dotenv/-/react-native-dotenv-0.2.0.tgz",
+ "integrity": "sha1-MRVRy2o1o9z+3mSL3tVcDj7OV50=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-dotenv": "0.1.1"
+ }
+ },
+ "react-native-elements": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/react-native-elements/-/react-native-elements-1.1.0.tgz",
+ "integrity": "sha512-n1eOL0kUdlH01zX7bn1p7qhYXn7kquqxYQ0oWlxoAck9t5Db/KeK5ViOsAk8seYSvAG6Pe7OxgzRFnMfFhng0Q==",
+ "requires": {
+ "color": "^3.1.0",
+ "deepmerge": "^3.1.0",
+ "hoist-non-react-statics": "^3.1.0",
+ "opencollective-postinstall": "^2.0.0",
+ "prop-types": "^15.5.8",
+ "react-native-ratings": "^6.3.0",
+ "react-native-status-bar-height": "^2.2.0"
+ }
+ },
+ "react-native-gesture-handler": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.4.1.tgz",
+ "integrity": "sha512-Ffcs+SbEbkGaal0CClYL+v7A9y4OA5G5lW01qrzjxaqASp5C8BfnWSKuqYKE00s6bLwE5L4xnlHkG0yIxAtbrQ==",
+ "requires": {
+ "hammerjs": "^2.0.8",
+ "hoist-non-react-statics": "^2.3.1",
+ "invariant": "^2.2.4",
+ "prop-types": "^15.7.2"
+ },
+ "dependencies": {
+ "hoist-non-react-statics": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
+ "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
+ }
+ }
+ },
+ "react-native-modal": {
+ "version": "11.3.1",
+ "resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-11.3.1.tgz",
+ "integrity": "sha512-3rRuXwvObknVijVNS8iamjMXWLjlb9xK90o+WtEcJ3C7HKuR2SOH578SoltIC6ZmVjO3vZwOApGVdSfR3LtPQg==",
+ "requires": {
+ "prop-types": "^15.6.2",
+ "react-native-animatable": "^1.3.1"
+ }
+ },
+ "react-native-modal-datetime-picker": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/react-native-modal-datetime-picker/-/react-native-modal-datetime-picker-7.5.0.tgz",
+ "integrity": "sha512-TtFS27IGhtg89LnIYIa8FrpNWbQENLiAAdfJPDdskv5VqIKgZSA0toOFYaAMRLeMiB8Urpf2/sAf0rdooCzu+g==",
+ "requires": {
+ "prop-types": "^15.7.2",
+ "react-native-modal": "^11.0.2"
+ }
+ },
+ "react-native-picker-select": {
+ "version": "6.3.3",
+ "resolved": "https://registry.npmjs.org/react-native-picker-select/-/react-native-picker-select-6.3.3.tgz",
+ "integrity": "sha512-9cSXWonugev+e0EHrV8FhzwkjAhpipLFXsGMv+Ns5xI47T9fyNrOXpSeSfgnmycbuAbWRVlJRhJZ9eDGUaNk7w==",
+ "requires": {
+ "lodash.isequal": "^4.5.0"
+ }
+ },
+ "react-native-ratings": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/react-native-ratings/-/react-native-ratings-6.4.0.tgz",
+ "integrity": "sha512-QvRJiEzjXZa7OL1MhJzbc50yUaOzPgjU6vGpkfIpWvgOyrjjkoQQqQ7EFvkoQMSEoRLD33eKTT3huJolScFoHQ==",
+ "requires": {
+ "lodash": "^4.17.4",
+ "prop-types": "^15.5.10"
+ }
+ },
+ "react-native-responsive-screen": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/react-native-responsive-screen/-/react-native-responsive-screen-1.3.0.tgz",
+ "integrity": "sha512-9ltQQZGYTClbzcXEkUHVHcSOzLoEOti/xLMjhDQCx9K5D1TwjswNe4b/cfiwF8Bwal+3Wscsb3FJTDR/tPORag=="
+ },
+ "react-native-safe-area-view": {
+ "version": "0.14.7",
+ "resolved": "https://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.14.7.tgz",
+ "integrity": "sha512-fmuBYpvKDJK33bimo4JXrK2BN2CGw7nof1y1LDRgzqv+FZ3eADSDGshprN8WeQqSZjQ20hJx1CiWk28Edg/v4Q==",
+ "requires": {
+ "hoist-non-react-statics": "^2.3.1"
+ },
+ "dependencies": {
+ "hoist-non-react-statics": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
+ "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
+ }
+ }
+ },
+ "react-native-screens": {
+ "version": "1.0.0-alpha.23",
+ "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-1.0.0-alpha.23.tgz",
+ "integrity": "sha512-tOxHGQUN83MTmQB4ghoQkibqOdGiX4JQEmeyEv96MKWO/x8T2PJv84ECUos9hD3blPRQwVwSpAid1PPPhrVEaw==",
+ "requires": {
+ "debounce": "^1.2.0"
+ }
+ },
+ "react-native-splash-screen": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/react-native-splash-screen/-/react-native-splash-screen-3.2.0.tgz",
+ "integrity": "sha512-Ls9qiNZzW/OLFoI25wfjjAcrf2DZ975hn2vr6U9gyuxi2nooVbzQeFoQS5vQcbCt9QX5NY8ASEEAtlLdIa6KVg=="
+ },
+ "react-native-status-bar-height": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/react-native-status-bar-height/-/react-native-status-bar-height-2.4.0.tgz",
+ "integrity": "sha512-pWvZFlyIHiuxLugLioq97vXiaGSovFXEyxt76wQtbq0gxv4dGXMPqYow46UmpwOgeJpBhqL1E0EKxnfJRrFz5w=="
+ },
+ "react-native-svg": {
+ "version": "9.10.2",
+ "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-9.10.2.tgz",
+ "integrity": "sha512-2A60JtU/A8//Fe5eVly81lMfiQGk+XlT+hVtB0XiPkWm5KSHyOa4W3tkW9baEt9pAz4GBTOVqTNvezWpguH5Nw=="
+ },
+ "react-native-tab-view": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-1.4.1.tgz",
+ "integrity": "sha512-Bke8KkDcDhvB/z0AS7MnQKMD2p6Kwfc1rSKlMOvg9CC5CnClQ2QEnhPSbwegKDYhUkBI92iH/BYy7hNSm5kbUQ==",
+ "requires": {
+ "prop-types": "^15.6.1"
+ }
+ },
+ "react-native-vector-icons": {
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-6.6.0.tgz",
+ "integrity": "sha512-MImKVx8JEvVVBnaShMr7/yTX4Y062JZMupht1T+IEgbqBj4aQeQ1z2SH4VHWKNtWtppk4kz9gYyUiMWqx6tNSw==",
+ "requires": {
+ "lodash": "^4.0.0",
+ "prop-types": "^15.6.2",
+ "yargs": "^13.2.2"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
+ "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "requires": {
+ "p-limit": "^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=="
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ }
+ },
+ "y18n": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
+ },
+ "yargs": {
+ "version": "13.3.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
+ "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
+ "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "react-native-view-shot": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/react-native-view-shot/-/react-native-view-shot-2.6.0.tgz",
+ "integrity": "sha512-yO9vWi/11m2hEJl8FrW1SMeVzFfPtMKh20MUInGqlsL0H8Ya2JGGlFfrBzx1KiFR2hFb5OdsTLYNtcVZtJ6pLQ=="
+ },
+ "react-native-web": {
+ "version": "0.11.7",
+ "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.11.7.tgz",
+ "integrity": "sha512-w1KAxX2FYLS2GAi3w3BnEZg/IUu7FdgHnLmFKHplRnHMV3u1OPB2EVA7ndNdfu7ds4Rn2OZjSXoNh6F61g3gkA==",
+ "requires": {
+ "array-find-index": "^1.0.2",
+ "create-react-class": "^15.6.2",
+ "debounce": "^1.2.0",
+ "deep-assign": "^3.0.0",
+ "fbjs": "^1.0.0",
+ "hyphenate-style-name": "^1.0.2",
+ "inline-style-prefixer": "^5.0.3",
+ "normalize-css-color": "^1.0.2",
+ "prop-types": "^15.6.0",
+ "react-timer-mixin": "^0.13.4"
+ }
+ },
+ "react-navigation": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/react-navigation/-/react-navigation-3.12.0.tgz",
+ "integrity": "sha512-Lr0l0lbZsFsajUx040I1HYm0yIfa+F9SCEeaNkS1eiey6k2G04eY5hYGZfo4i3kaMj99Yi8tuRcSN7DS0QZu8w==",
+ "requires": {
+ "@react-navigation/core": "~3.5.0",
+ "@react-navigation/native": "~3.6.0",
+ "react-navigation-drawer": "~1.4.0",
+ "react-navigation-stack": "~1.5.0",
+ "react-navigation-tabs": "~1.2.0"
+ }
+ },
+ "react-navigation-drawer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/react-navigation-drawer/-/react-navigation-drawer-1.4.0.tgz",
+ "integrity": "sha512-ZyWBozcjB2aZ7vwCALv90cYA2NpDjM+WALaiYRshvPvue8l7cqynePbHK8GhlMGyJDwZqp4MxQmu8u1XAKp3Bw==",
+ "requires": {
+ "react-native-tab-view": "^1.2.0"
+ }
+ },
+ "react-navigation-redux-helpers": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/react-navigation-redux-helpers/-/react-navigation-redux-helpers-3.0.2.tgz",
+ "integrity": "sha512-+z7/eBGBpws/W3ffu7ayEl1YFMAbXO3Sgul3KIDyESI1BbmfSvKD2aRMEfE7AlO+58fJJsqWUMhNw+VACAdHjw==",
+ "requires": {
+ "invariant": "^2.2.2"
+ }
+ },
+ "react-navigation-stack": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/react-navigation-stack/-/react-navigation-stack-1.5.0.tgz",
+ "integrity": "sha512-XwgdQY8c7PguPw/cx6GCRMnYMe6P/izzOpeOfE8g6sWrWW2vs/vwodRb9a820QePhX0I7dkgxdnTSq+o7/R49Q==",
+ "requires": {
+ "prop-types": "^15.7.2"
+ }
+ },
+ "react-navigation-tabs": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/react-navigation-tabs/-/react-navigation-tabs-1.2.0.tgz",
+ "integrity": "sha512-I6vq3XX4ub9KhWQzcrggznls+2Z2C6w2ro46vokDGGvJ02CBpQRar7J0ETV29Ot5AJY67HucNUmZdH3yDFckmQ==",
+ "requires": {
+ "hoist-non-react-statics": "^2.5.0",
+ "prop-types": "^15.6.1",
+ "react-native-tab-view": "^1.4.1"
+ },
+ "dependencies": {
+ "hoist-non-react-statics": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
+ "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
+ }
+ }
+ },
+ "react-proxy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-1.1.8.tgz",
+ "integrity": "sha1-nb/Z2SdSjDqp9ETkVYw3gwq4wmo=",
+ "requires": {
+ "lodash": "^4.6.1",
+ "react-deep-force-update": "^1.0.0"
+ }
+ },
+ "react-redux": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.1.1.tgz",
+ "integrity": "sha512-QsW0vcmVVdNQzEkrgzh2W3Ksvr8cqpAv5FhEk7tNEft+5pp7rXxAudTz3VOPawRkLIepItpkEIyLcN/VVXzjTg==",
+ "requires": {
+ "@babel/runtime": "^7.5.5",
+ "hoist-non-react-statics": "^3.3.0",
+ "invariant": "^2.2.4",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.7.2",
+ "react-is": "^16.9.0"
+ }
+ },
+ "react-timer-mixin": {
+ "version": "0.13.4",
+ "resolved": "https://registry.npmjs.org/react-timer-mixin/-/react-timer-mixin-0.13.4.tgz",
+ "integrity": "sha512-4+ow23tp/Tv7hBM5Az5/Be/eKKF7DIvJ09voz5LyHGQaqqz9WV8YMs31eFvcYQs7d451LSg7kDJV70XYN/Ug/Q=="
+ },
+ "react-transform-hmr": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/react-transform-hmr/-/react-transform-hmr-1.0.4.tgz",
+ "integrity": "sha1-4aQL0Krvxy6N/Xp82gmvhQZjl7s=",
+ "requires": {
+ "global": "^4.3.0",
+ "react-proxy": "^1.1.7"
+ }
+ },
+ "read-pkg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "requires": {
+ "load-json-file": "^2.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^2.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "requires": {
+ "find-up": "^2.0.0",
+ "read-pkg": "^2.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "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"
+ }
+ },
+ "reduce-reducers": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/reduce-reducers/-/reduce-reducers-0.1.5.tgz",
+ "integrity": "sha512-uoVmQnZQ+BtKKDKpBdbBri5SLNyIK9ULZGOA504++VbHcwouWE+fJDIo8AuESPF9/EYSkI0v05LDEQK6stCbTA=="
+ },
+ "redux": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.4.tgz",
+ "integrity": "sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "symbol-observable": "^1.2.0"
+ }
+ },
+ "redux-actions": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/redux-actions/-/redux-actions-0.10.1.tgz",
+ "integrity": "sha1-u0Qu433ZZDqUkz5AceCJ9DVYcTU=",
+ "requires": {
+ "reduce-reducers": "^0.1.0"
+ }
+ },
+ "redux-form": {
+ "version": "8.2.6",
+ "resolved": "https://registry.npmjs.org/redux-form/-/redux-form-8.2.6.tgz",
+ "integrity": "sha512-krmF7wl1C753BYpEpWIVJ5NM4lUJZFZc5GFUVgblT+jprB99VVBDyBcgrZM3gWWLOcncFyNsHcKNQQcFg8Uanw==",
+ "requires": {
+ "@babel/runtime": "^7.2.0",
+ "es6-error": "^4.1.1",
+ "hoist-non-react-statics": "^3.2.1",
+ "immutable": "*",
+ "invariant": "^2.2.4",
+ "is-promise": "^2.1.0",
+ "lodash": "^4.17.15",
+ "lodash-es": "^4.17.15",
+ "prop-types": "^15.6.1",
+ "react-is": "^16.7.0",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "redux-persist": {
+ "version": "5.10.0",
+ "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-5.10.0.tgz",
+ "integrity": "sha512-sSJAzNq7zka3qVHKce1hbvqf0Vf5DuTVm7dr4GtsqQVOexnrvbV47RWFiPxQ8fscnyiuWyD2O92DOxPl0tGCRg=="
+ },
+ "redux-saga": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.0.5.tgz",
+ "integrity": "sha512-ytGFtaHyz6NQMQp7/LpQ/6CtOkbPRCcAeljUpc4c95hRm5U6dFvrRiZHeBPuQZ56L3oXfTX3hiYh8/3ZudsiNg==",
+ "requires": {
+ "@redux-saga/core": "^1.0.3"
+ }
+ },
+ "redux-storage": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/redux-storage/-/redux-storage-4.1.2.tgz",
+ "integrity": "sha1-4G9L3u4mKurZEy/J9+rcZ+n5vqI=",
+ "requires": {
+ "lodash.isfunction": "^3.0.8",
+ "lodash.isobject": "^3.0.2",
+ "loose-envify": "^1.2.0",
+ "redux-actions": "^0.10.1",
+ "redux-storage-merger-simple": "^1.0.2"
+ }
+ },
+ "redux-storage-merger-simple": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/redux-storage-merger-simple/-/redux-storage-merger-simple-1.0.5.tgz",
+ "integrity": "sha1-KaKIaw53DZtwgRrKgAqo766J+3M=",
+ "requires": {
+ "lodash.isobject": "^3.0.2",
+ "lodash.merge": "^4.3.1"
+ }
+ },
+ "regenerate": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
+ "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg=="
+ },
+ "regenerate-unicode-properties": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz",
+ "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==",
+ "requires": {
+ "regenerate": "^1.4.0"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+ },
+ "regenerator-transform": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz",
+ "integrity": "sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==",
+ "requires": {
+ "private": "^0.1.6"
+ }
+ },
+ "regex-cache": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
+ "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==",
+ "requires": {
+ "is-equal-shallow": "^0.1.3"
+ }
+ },
+ "regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "requires": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "regexp-tree": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.12.tgz",
+ "integrity": "sha512-TsXZ8+cv2uxMEkLfgwO0E068gsNMLfuYwMMhiUxf0Kw2Vcgzq93vgl6wIlIYuPmfMqMjfQ9zAporiozqCnwLuQ=="
+ },
+ "regexpp": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw=="
+ },
+ "regexpu-core": {
+ "version": "4.5.5",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.5.tgz",
+ "integrity": "sha512-FpI67+ky9J+cDizQUJlIlNZFKual/lUkFr1AG6zOCpwZ9cLrg8UUVakyUQJD7fCDIe9Z2nwTQJNPyonatNmDFQ==",
+ "requires": {
+ "regenerate": "^1.4.0",
+ "regenerate-unicode-properties": "^8.1.0",
+ "regjsgen": "^0.5.0",
+ "regjsparser": "^0.6.0",
+ "unicode-match-property-ecmascript": "^1.0.4",
+ "unicode-match-property-value-ecmascript": "^1.1.0"
+ }
+ },
+ "regjsgen": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz",
+ "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA=="
+ },
+ "regjsparser": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz",
+ "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==",
+ "requires": {
+ "jsesc": "~0.5.0"
+ },
+ "dependencies": {
+ "jsesc": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0="
+ }
+ }
+ },
+ "remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
+ },
+ "repeat-element": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+ "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g=="
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+ },
+ "require-main-filename": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
+ },
+ "reselect": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-3.0.1.tgz",
+ "integrity": "sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc="
+ },
+ "resolve": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
+ "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
+ },
+ "resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
+ },
+ "restore-cursor": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+ "requires": {
+ "onetime": "^2.0.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "rsvp": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz",
+ "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw=="
+ },
+ "rtl-detect": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.2.tgz",
+ "integrity": "sha512-5X1422hvphzg2a/bo4tIDbjFjbJUOaPZwqE6dnyyxqwFqfR+tBcvfqapJr0o0VygATVCGKiODEewhZtKF+90AA=="
+ },
+ "run-async": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+ "requires": {
+ "is-promise": "^2.1.0"
+ }
+ },
+ "rx-lite": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
+ "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ="
+ },
+ "rx-lite-aggregates": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
+ "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
+ "requires": {
+ "rx-lite": "*"
+ }
+ },
+ "rxjs": {
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz",
+ "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "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=="
+ },
+ "safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "requires": {
+ "ret": "~0.1.10"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "sane": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/sane/-/sane-3.1.0.tgz",
+ "integrity": "sha512-G5GClRRxT1cELXfdAq7UKtUsv8q/ZC5k8lQGmjEm4HcAl3HzBy68iglyNCmw4+0tiXPCBZntslHlRhbnsSws+Q==",
+ "requires": {
+ "anymatch": "^2.0.0",
+ "capture-exit": "^1.2.0",
+ "exec-sh": "^0.2.0",
+ "execa": "^1.0.0",
+ "fb-watchman": "^2.0.0",
+ "fsevents": "^1.2.3",
+ "micromatch": "^3.1.4",
+ "minimist": "^1.1.1",
+ "walker": "~1.0.5",
+ "watch": "~0.18.0"
+ },
+ "dependencies": {
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
+ }
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ }
+ }
+ },
+ "sax": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.6.tgz",
+ "integrity": "sha1-XWFr6KXmB9VOEUr65Vt+ry/MMkA="
+ },
+ "scheduler": {
+ "version": "0.13.6",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz",
+ "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ }
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+ },
+ "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": {
+ "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=="
+ }
+ }
+ },
+ "serialize-error": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
+ "integrity": "sha1-ULZ51WNc34Rme9yOWa9OW4HV9go="
+ },
+ "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"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+ },
+ "set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
+ },
+ "setprototypeof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
+ },
+ "shell-quote": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
+ "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=",
+ "requires": {
+ "array-filter": "~0.0.0",
+ "array-map": "~0.0.0",
+ "array-reduce": "~0.0.0",
+ "jsonify": "~0.0.0"
+ }
+ },
+ "shellwords": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
+ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww=="
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
+ },
+ "simple-plist": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.0.0.tgz",
+ "integrity": "sha512-043L2rO80LVF7zfZ+fqhsEkoJFvW8o59rt/l4ctx1TJWoTx7/jkiS1R5TatD15Z1oYnuLJytzE7gcnnBuIPL2g==",
+ "requires": {
+ "bplist-creator": "0.0.7",
+ "bplist-parser": "0.1.1",
+ "plist": "^3.0.1"
+ }
+ },
+ "simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+ "requires": {
+ "is-arrayish": "^0.3.1"
+ },
+ "dependencies": {
+ "is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+ }
+ }
+ },
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="
+ },
+ "slice-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+ "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "astral-regex": "^1.0.0",
+ "is-fullwidth-code-point": "^2.0.0"
+ }
+ },
+ "slide": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
+ "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
+ },
+ "snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "requires": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "requires": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
+ }
+ }
+ },
+ "snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "requires": {
+ "kind-of": "^3.2.0"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+ },
+ "source-map-resolve": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
+ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
+ "requires": {
+ "atob": "^2.1.1",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-support": {
+ "version": "0.5.13",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
+ "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ }
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM="
+ },
+ "spdx-correct": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
+ "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
+ "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA=="
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+ "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q=="
+ },
+ "split-on-first": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
+ "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
+ },
+ "split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "requires": {
+ "extend-shallow": "^3.0.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
+ },
+ "stacktrace-parser": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.4.tgz",
+ "integrity": "sha1-ATl5IuX2Ls8whFUiyVxP4dJefU4="
+ },
+ "static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+ "requires": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+ },
+ "stream-buffers": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz",
+ "integrity": "sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ="
+ },
+ "strict-uri-encode": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
+ "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "string.prototype.trimleft": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz",
+ "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "function-bind": "^1.1.1"
+ }
+ },
+ "string.prototype.trimright": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz",
+ "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==",
+ "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==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
+ },
+ "strip-eof": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
+ },
+ "strip-json-comments": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
+ "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "symbol-observable": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
+ },
+ "table": {
+ "version": "5.4.6",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+ "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.10.2",
+ "lodash": "^4.17.14",
+ "slice-ansi": "^2.1.0",
+ "string-width": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "temp": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz",
+ "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=",
+ "requires": {
+ "os-tmpdir": "^1.0.0",
+ "rimraf": "~2.2.6"
+ },
+ "dependencies": {
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
+ "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI="
+ }
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "throat": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz",
+ "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo="
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+ },
+ "through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "requires": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "time-stamp": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
+ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM="
+ },
+ "tiny-queue": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/tiny-queue/-/tiny-queue-0.2.1.tgz",
+ "integrity": "sha1-JaZ/LG4lOyypQZd7XvdELvl6YEY="
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "tmpl": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
+ "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE="
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
+ },
+ "to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "requires": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ }
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+ },
+ "trim-right": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
+ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM="
+ },
+ "tslib": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
+ "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
+ },
+ "tsutils": {
+ "version": "3.17.1",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
+ "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==",
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+ },
+ "typescript-compare": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz",
+ "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==",
+ "requires": {
+ "typescript-logic": "^0.0.0"
+ }
+ },
+ "typescript-logic": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz",
+ "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q=="
+ },
+ "typescript-tuple": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz",
+ "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==",
+ "requires": {
+ "typescript-compare": "^0.0.2"
+ }
+ },
+ "ua-parser-js": {
+ "version": "0.7.20",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.20.tgz",
+ "integrity": "sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw=="
+ },
+ "uglify-es": {
+ "version": "3.3.9",
+ "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
+ "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
+ "requires": {
+ "commander": "~2.13.0",
+ "source-map": "~0.6.1"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
+ "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA=="
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ }
+ }
+ },
+ "ultron": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
+ "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po="
+ },
+ "unicode-canonical-property-names-ecmascript": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
+ "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ=="
+ },
+ "unicode-match-property-ecmascript": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz",
+ "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==",
+ "requires": {
+ "unicode-canonical-property-names-ecmascript": "^1.0.4",
+ "unicode-property-aliases-ecmascript": "^1.0.4"
+ }
+ },
+ "unicode-match-property-value-ecmascript": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz",
+ "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g=="
+ },
+ "unicode-property-aliases-ecmascript": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz",
+ "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw=="
+ },
+ "unimodules-barcode-scanner-interface": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unimodules-barcode-scanner-interface/-/unimodules-barcode-scanner-interface-3.0.0.tgz",
+ "integrity": "sha512-EtJBfKU5VgZbyIfIZwyWfUo59pIgW6s7YGzlpj9jk4UWKyqqhYT/FoaZqudCJcPcfh2eYxkc9VxBGieRBpQrzg=="
+ },
+ "unimodules-camera-interface": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unimodules-camera-interface/-/unimodules-camera-interface-3.0.0.tgz",
+ "integrity": "sha512-STjf1FAdYlN27ilJSR4kIUYyHTPrkQSR/mEg4S4pZX6tazmcuG2KzLCXCoV+xMWsrwmsMBjgLzw6yzg87N5Ydw=="
+ },
+ "unimodules-constants-interface": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unimodules-constants-interface/-/unimodules-constants-interface-3.0.0.tgz",
+ "integrity": "sha512-S4ap11UJH7D+Y4fXC7DyMNAkqIWD8B7rNCTS30wAF9beHXMZa1Od66rkJgSHqFRURy06h+Jr7qfJm9H5mtMz8Q=="
+ },
+ "unimodules-face-detector-interface": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unimodules-face-detector-interface/-/unimodules-face-detector-interface-3.0.0.tgz",
+ "integrity": "sha512-fMQ3ZnhdOjbQ5ZXW62s/t1bbqBaenxzVIcgVEcwvLIFek0mx/EMHFkySgFkFjU11icUvaPEXW1yJtkK4QEpLhg=="
+ },
+ "unimodules-file-system-interface": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unimodules-file-system-interface/-/unimodules-file-system-interface-3.0.0.tgz",
+ "integrity": "sha512-LkLIKRE3CwsXLRFw8vx0++Cfjj+pAvvidVb7yhGWKFmNlVaWUW9Z8jkhFLBFXDsGFAOU69bUTrz25jmB2MRt0Q=="
+ },
+ "unimodules-font-interface": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unimodules-font-interface/-/unimodules-font-interface-3.0.0.tgz",
+ "integrity": "sha512-DOQI0uTn7CGvA9lNUuiTWfQYuKQEM8LZKn6gNS8G+HVHVb+TZl/37qdhuoMBi5jkAZ4VOD/GpgnPv8qr0pJi1Q=="
+ },
+ "unimodules-image-loader-interface": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unimodules-image-loader-interface/-/unimodules-image-loader-interface-3.0.0.tgz",
+ "integrity": "sha512-hC/VWdT33GkOZ4FLaqPoKGNKxhw+miFhM+7Re57snWIWYewSv0lRvCqqwc/hbGLocvd2qF3YYrBx9woqPI8NzA=="
+ },
+ "unimodules-permissions-interface": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unimodules-permissions-interface/-/unimodules-permissions-interface-3.0.0.tgz",
+ "integrity": "sha512-rfyGDBMtO8IOlk9hJN44EKz7vk6nt/PXByAumsptRdgsd+knokMlaWGYatrxKW2g/08WUbEkgKspvMxjJ0M1Tg=="
+ },
+ "unimodules-sensors-interface": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unimodules-sensors-interface/-/unimodules-sensors-interface-3.0.0.tgz",
+ "integrity": "sha512-1JJT/lqCfxHqUSJc3o6b0WUply/lFOJjcuzN0QcAfmdAW8d+lEXA7BJ7DV/Nn/OKpMlHriEyxkM+FoGoXKJJcg=="
+ },
+ "unimodules-task-manager-interface": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unimodules-task-manager-interface/-/unimodules-task-manager-interface-3.0.0.tgz",
+ "integrity": "sha512-og4UiUOxc7PqT8uQQqXY+pOBvdS204xmgyUG2AjM2L3kVsw/6WH4pIW084WG8/e9M5SLsSXdrjecIUBQ/zLf8w=="
+ },
+ "union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ }
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+ },
+ "unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+ "requires": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+ "requires": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E="
+ }
+ }
+ },
+ "uri-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
+ },
+ "url-parse": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
+ "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
+ "requires": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+ },
+ "uuid": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
+ "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
+ },
+ "uuid-js": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/uuid-js/-/uuid-js-0.7.5.tgz",
+ "integrity": "sha1-bIhtAqU9LUDc8l2RoXC0p7JblNA="
+ },
+ "v8-compile-cache": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz",
+ "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+ },
+ "walker": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz",
+ "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=",
+ "requires": {
+ "makeerror": "1.0.x"
+ }
+ },
+ "watch": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/watch/-/watch-0.18.0.tgz",
+ "integrity": "sha1-KAlUdsbffJDJYxOJkMClQj60uYY=",
+ "requires": {
+ "exec-sh": "^0.2.0",
+ "minimist": "^1.2.0"
+ }
+ },
+ "whatwg-fetch": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
+ "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
+ },
+ "wrap-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1"
+ },
+ "dependencies": {
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "write": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+ "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^0.5.1"
+ }
+ },
+ "write-file-atomic": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz",
+ "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=",
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "imurmurhash": "^0.1.4",
+ "slide": "^1.1.5"
+ }
+ },
+ "ws": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz",
+ "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==",
+ "requires": {
+ "options": ">=0.0.5",
+ "ultron": "1.0.x"
+ }
+ },
+ "xcode": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/xcode/-/xcode-2.0.0.tgz",
+ "integrity": "sha512-5xF6RCjAdDEiEsbbZaS/gBRt3jZ/177otZcpoLCjGN/u1LrfgH7/Sgeeavpr/jELpyDqN2im3AKosl2G2W8hfw==",
+ "requires": {
+ "simple-plist": "^1.0.0",
+ "uuid": "^3.3.2"
+ }
+ },
+ "xmlbuilder": {
+ "version": "9.0.7",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
+ "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
+ },
+ "xmldoc": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-0.4.0.tgz",
+ "integrity": "sha1-0lciS+g5PqrL+DfvIn/Y7CWzaIg=",
+ "requires": {
+ "sax": "~1.1.1"
+ }
+ },
+ "xmldom": {
+ "version": "0.1.27",
+ "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz",
+ "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk="
+ },
+ "xpipe": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/xpipe/-/xpipe-1.0.5.tgz",
+ "integrity": "sha1-jdi/Rfw/f1Xw4FS4ePQ6YmFNr98="
+ },
+ "xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+ },
+ "y18n": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+ },
+ "yargs": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz",
+ "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=",
+ "requires": {
+ "camelcase": "^4.1.0",
+ "cliui": "^3.2.0",
+ "decamelize": "^1.1.1",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^2.0.0",
+ "read-pkg-up": "^2.0.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^2.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^3.2.1",
+ "yargs-parser": "^7.0.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz",
+ "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=",
+ "requires": {
+ "camelcase": "^4.1.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..3c4e2b27
--- /dev/null
+++ b/package.json
@@ -0,0 +1,60 @@
+{
+ "main": "node_modules/expo/AppEntry.js",
+ "scripts": {
+ "start": "expo start",
+ "android": "expo start --android",
+ "ios": "expo start --ios",
+ "web": "expo start --web",
+ "eject": "expo eject",
+ "prettier-fix": "prettier --config .prettierlc.js --write 'src/**/*.js'",
+ "lints": "yarn prettier-fix"
+ },
+ "dependencies": {
+ "@react-native-community/eslint-config": "0.0.5",
+ "@react-native-community/netinfo": "^4.4.0",
+ "babel-eslint": "^10.0.3",
+ "eslint-config-standard": "^14.1.0",
+ "eslint-plugin-flowtype": "^4.3.0",
+ "expo": "^34.0.1",
+ "expo-constants": "~6.0.0",
+ "expo-file-system": "^7.0.0",
+ "expo-image-picker": "^6.0.0",
+ "expo-intent-launcher": "~6.0.0",
+ "expo-linear-gradient": "^6.0.0",
+ "expo-localization": "^6.0.0",
+ "expo-permissions": "~6.0.0",
+ "i18n-js": "^3.3.0",
+ "lodash.get": "^4.4.2",
+ "moment": "^2.24.0",
+ "query-string": "^6.8.3",
+ "react": "16.8.3",
+ "react-dom": "^16.8.6",
+ "react-native": "https://github.com/expo/react-native/archive/sdk-34.0.0.tar.gz",
+ "react-native-actionsheet": "^2.4.2",
+ "react-native-elements": "^1.1.0",
+ "react-native-gesture-handler": "^1.3.0",
+ "react-native-modal-datetime-picker": "^7.5.0",
+ "react-native-picker-select": "^6.3.3",
+ "react-native-responsive-screen": "^1.3.0",
+ "react-native-splash-screen": "^3.2.0",
+ "react-native-svg": "^9.9.9",
+ "react-native-vector-icons": "^6.6.0",
+ "react-native-web": "^0.11.4",
+ "react-navigation": "^3.12.0",
+ "react-navigation-redux-helpers": "^3.0.2",
+ "react-redux": "^7.1.1",
+ "redux-form": "^8.2.6",
+ "redux-persist": "^5.10.0",
+ "redux-saga": "^1.0.5",
+ "redux-storage": "^4.1.2"
+ },
+ "devDependencies": {
+ "babel-preset-expo": "^6.0.0",
+ "eslint": "6.3.0",
+ "eslint-config-prettier": "6.3.0",
+ "eslint-plugin-prettier": "3.1.0",
+ "prettier": "1.18.2",
+ "react-native-dotenv": "^0.2.0"
+ },
+ "private": true
+}
diff --git a/src/api/api.js b/src/api/api.js
new file mode 100644
index 00000000..07c87e7d
--- /dev/null
+++ b/src/api/api.js
@@ -0,0 +1,49 @@
+import queryString from 'query-string';
+
+export const GET_METHOD = 'GET'
+export const GET_EDIT_METHOD = 'GET_EDIT'
+export const GET_SLASH_METHOD = 'GET_SLASH'
+export const POST_METHOD = 'POST'
+export const PUT_METHOD = 'PUT'
+export const DELETE_METHOD = 'DELETE'
+
+export const COUNTRY_API = 'countries'
+export const STATE_API = 'states'
+export const CITY_API = 'cities'
+
+export const CUSTOMERS_API = 'customers'
+
+
+const apiMethod = (url, method, params) => {
+
+ let { id } = params
+
+ switch (method) {
+ case GET_METHOD:
+ return `${url}?${queryString.stringify(params)}`;
+
+ case GET_EDIT_METHOD:
+ return `${url}/${params}/edit`;
+
+ case GET_SLASH_METHOD:
+ return `${url}/${params}`;
+
+ case POST_METHOD:
+ return url;
+
+ case PUT_METHOD:
+ return `${url}/${id}`;
+
+ case DELETE_METHOD:
+ return `${url}/${id}`;
+
+ default:
+ return url;
+ }
+}
+
+export const API_URL = ({ ...path }) => {
+ const { method, params, apiName } = path
+
+ return apiMethod(apiName, method, params, apiName)
+}
diff --git a/src/api/compareVersion.js b/src/api/compareVersion.js
new file mode 100644
index 00000000..ddb12c89
--- /dev/null
+++ b/src/api/compareVersion.js
@@ -0,0 +1,44 @@
+export default function (v1, v2, options) {
+ const lexicographical = options && options.lexicographical;
+ const zeroExtend = options && options.zeroExtend;
+ let v1parts = v1.split('.');
+ let v2parts = v2.split('.');
+
+ function isValidPart(x) {
+ return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x);
+ }
+
+ if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) {
+ return NaN;
+ }
+
+ if (zeroExtend) {
+ while (v1parts.length < v2parts.length) v1parts.push('0');
+ while (v2parts.length < v1parts.length) v2parts.push('0');
+ }
+
+ if (!lexicographical) {
+ v1parts = v1parts.map(Number);
+ v2parts = v2parts.map(Number);
+ }
+
+ for (let i = 0; i < v1parts.length; ++i) {
+ if (v2parts.length == i) {
+ return 1;
+ }
+
+ if (v1parts[i] == v2parts[i]) {
+ continue;
+ } else if (v1parts[i] > v2parts[i]) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+
+ if (v1parts.length != v2parts.length) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/api/consts/core.js b/src/api/consts/core.js
new file mode 100644
index 00000000..0d3b3dc6
--- /dev/null
+++ b/src/api/consts/core.js
@@ -0,0 +1,53 @@
+export const NAVIGATION_PERSIST_KEY = 'persist:root';
+
+export const BUTTON_TYPE = {
+ SOLID: 'solid',
+ OUTLINE: 'outline',
+ CLEAR: 'clear',
+};
+
+export const BUTTON_COLOR = {
+ PRIMARY: 'primary',
+ PRIMARY_LIGHT: 'primaryLight',
+ SUCCESS: 'success',
+ SUCCESS_LIGHT: 'successLight',
+ SUCCESS_DARK: 'successDark',
+ INFO: 'info',
+ DANGER: 'danger',
+ DANGER_LIGHT: 'dangerLight',
+ DANGER_DARK: 'dangerDark',
+ WARNING: 'warning',
+ WARNING_LIGHT: 'warningLight',
+ WARNING_DARK: 'warningDark',
+ DARK: 'dark',
+ DARK2: 'dark2',
+ DARK3: 'dark3',
+ VERY_LIGHT_GRAY: 'veryLightGray',
+ LIGHT_GRAY: 'lightGray',
+ DARK_GRAY: 'darkGray',
+ VERY_DARK_GRAY: 'veryDarkGray',
+ PINK: 'pink',
+ LIGHT_GREEN: 'lightGreen',
+ WHITE: 'white',
+};
+
+// type
+export const ENDPOINT_SETTINGS = 'global/ENDPOINT_SETTINGS'
+export const ENDPOINT_INITIAL = 'global/ENDPOINT_INITIAL'
+
+/**
+ * Date format for sending backend request
+ */
+export const DATE_FORMAT = 'DD/MM/YYYY';
+
+
+/**
+ * Global action const
+ */
+export const SET_GLOBAL_BOOTSTRAP = 'SET_GLOBAL_BOOTSTRAP';
+export const SAVE_ENDPOINT_API = 'SAVE_ENDPOINT_API';
+export const SET_SETTINGS = 'SET_SETTINGS';
+export const GLOBAL_TRIGGER_SPINNER = 'GLOBAL_TRIGGER_SPINNER';
+export const GET_APP_VERSION = 'GET_APP_VERSION';
+export const SET_APP_VERSION = 'SET_APP_VERSION';
+
diff --git a/src/api/consts/index.js b/src/api/consts/index.js
new file mode 100644
index 00000000..684f9576
--- /dev/null
+++ b/src/api/consts/index.js
@@ -0,0 +1,3 @@
+
+export * from './core';
+export * from './regex';
diff --git a/src/api/consts/regex.js b/src/api/consts/regex.js
new file mode 100644
index 00000000..ced6928c
--- /dev/null
+++ b/src/api/consts/regex.js
@@ -0,0 +1,3 @@
+export const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+
+export const URL_REGEX = /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/;
\ No newline at end of file
diff --git a/src/api/global.js b/src/api/global.js
new file mode 100644
index 00000000..3d937e74
--- /dev/null
+++ b/src/api/global.js
@@ -0,0 +1,63 @@
+import * as Font from 'expo-font';
+import { Alert } from 'react-native';
+import Poppins from '../assets/fonts/Poppins-Regular.ttf';
+import PoppinsLight from '../assets/fonts/Poppins-Light.ttf';
+import PoppinsMedium from '../assets/fonts/Poppins-Medium.ttf';
+import PoppinsSemiBold from '../assets/fonts/Poppins-SemiBold.ttf';
+import PoppinsBold from '../assets/fonts/Poppins-Bold.ttf';
+
+export const loadFonts = async ({ afterLoad }) => {
+ await Font.loadAsync({
+ Poppins: Poppins,
+ 'Poppins-light': PoppinsLight,
+ 'Poppins-medium': PoppinsMedium,
+ 'Poppins-semi-bold': PoppinsSemiBold,
+ 'Poppins-bold': PoppinsBold,
+ });
+
+ afterLoad && afterLoad();
+};
+
+export const formatTaxTypes = (taxes) => {
+ let taxTypeList = []
+
+ if (typeof taxes !== 'undefined' && taxes.length != 0) {
+ taxTypeList = taxes.map((tax) => {
+
+ const { name, percent, description } = tax
+
+ return {
+ title: name,
+ subtitle: {
+ title: description,
+ },
+ rightTitle: `${percent} %`,
+ fullItem: tax
+ }
+
+ })
+ }
+ return taxTypeList
+}
+
+export const MAX_LENGTH = 255
+
+// Alert
+// -----------------------------------------
+export const alertMe = ({ title = '', desc = '' }) => {
+
+ Alert.alert(
+ title,
+ desc,
+ [
+ {
+ text: 'OK',
+ onPress: () => 'cancel',
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+}
+
+
diff --git a/src/api/helper.js b/src/api/helper.js
new file mode 100644
index 00000000..1a88147b
--- /dev/null
+++ b/src/api/helper.js
@@ -0,0 +1,120 @@
+import React, { Fragment } from 'react';
+import { Platform, findNodeHandle, Dimensions } from 'react-native';
+import Constants from 'expo-constants';
+import NetInfo from "@react-native-community/netinfo";
+import { Text } from 'react-native-elements';
+import moment from 'moment';
+import { fonts } from '../styles/fonts';
+
+const model = Constants.deviceName.toLowerCase();
+
+// export const isIPhoneX = () => model.indexOf('iphone x') !== -1;
+
+export function isIPhoneX() {
+ const dimension = Dimensions.get('window');
+ return (
+ Platform.OS === 'ios' && !Platform.isPad && !Platform.isTVOS &&
+ ((dimension.height === 812 || dimension.width === 812) || (dimension.height === 896 || dimension.width === 896))
+ );
+}
+
+export const trim = (data) => {
+ return Object.keys(data).reduce((accumulator, key) => {
+ accumulator[key] = typeof data[key] === 'string' ? data[key].trim() : data[key];
+
+ return accumulator;
+ }, {});
+};
+
+export const isIosPlatform = () => Platform.OS === 'ios';
+
+export const definePlatformParam = (ios, android) => (isIosPlatform() ? ios : android);
+
+export const scrollToInput = ({ scrollView }, { target }) => {
+ scrollView.scrollToFocusedInput(findNodeHandle(target));
+};
+
+export const pick = (object = {}, keys = []) => {
+ const result = {};
+
+ keys.forEach((key) => {
+ result[key] = object[key];
+ });
+
+ return result;
+};
+
+export const formatMoney = (amount, currency = 0) => {
+ amount = (amount / 100)
+
+ if (!currency) {
+ currency = { precision: 2, thousand_separator: ',', decimal_separator: '.', symbol: '$' }
+ }
+
+ let { precision, decimal_separator, thousand_separator, symbol } = currency
+
+ try {
+ precision = Math.abs(precision)
+ precision = isNaN(precision) ? 2 : precision
+
+ const negativeSign = amount < 0 ? '-' : ''
+
+ let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(precision)).toString()
+ let j = (i.length > 3) ? i.length % 3 : 0
+
+ // return symbol + ' ' + negativeSign + (j ? i.substr(0, j) + thousand_separator : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousand_separator) + (precision ? decimal_separator + Math.abs(amount - i).toFixed(precision).slice(2) : '')
+ return { symbol, money: negativeSign + (j ? i.substr(0, j) + thousand_separator : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousand_separator) + (precision ? decimal_separator + Math.abs(amount - i).toFixed(precision).slice(2) : '') }
+
+ } catch (e) {
+ // console.log(e)
+ }
+}
+
+export const getConditionStyles = (styles: IGetConditionStyles) => {
+ let commonStyles = {};
+
+ if (typeof styles === 'object' && Array.isArray(styles)) {
+ styles
+ .filter((v) => !!v && typeof v === 'object')
+ .forEach((v) => {
+ const { condition, style } = v;
+
+ if (condition) {
+ commonStyles = { ...commonStyles, ...style };
+ } else {
+ commonStyles = { ...commonStyles, ...v };
+ }
+ });
+ }
+
+ return commonStyles;
+};
+
+export const checkConnection = async (callback) => {
+
+ let state = await NetInfo.fetch()
+
+ let { isConnected } = state
+
+ if (callback && typeof callback === 'function') {
+ callback(isConnected);
+ }
+
+ return isConnected;
+}
+
+export const checkExpiredToken = (expiresIn) => {
+ if (expiresIn) {
+ return !moment().isBefore(moment(expiresIn))
+ }
+ return true
+}
+
+export const headerTitle = ({ marginLeft = -7, marginRight = -12 }) => {
+ return {
+ marginLeft: isIPhoneX() ? 0 : marginLeft,
+ marginRight: isIPhoneX() ? 0 : marginRight,
+ textAlign: "center",
+ fontFamily: fonts.poppins,
+ }
+}
\ No newline at end of file
diff --git a/src/api/lang/en.js b/src/api/lang/en.js
new file mode 100644
index 00000000..19fe2ba7
--- /dev/null
+++ b/src/api/lang/en.js
@@ -0,0 +1,633 @@
+export const en = {
+
+ header: {
+
+ // Invoices
+ invoices: "Invoices",
+ addInvoice: "Add Invoice",
+ editInvoice: "Edit Invoice",
+
+ // Estimates
+ estimates: "Estimates",
+ addEstimate: "Add Estimate",
+ editEstimate: "Edit Estimate",
+
+ // Templates
+ template: "Select a Template",
+
+ // Customers
+ customers: "Customers",
+ addCustomer: "Add Customer",
+ editCustomer: "Edit Customer",
+
+ selectCurrency: "Select Currency",
+ billingAddress: "Billing Address",
+ shippingAddress: "Shipping Address",
+
+ country: "Select Country",
+ state: "Select State",
+ city: "Select City",
+
+ // Payments
+ payments: "Payments",
+ addPayment: "Add Payment",
+ editPayment: "Edit Payment",
+
+
+ // Expenses
+ expenses: "Expenses",
+ addExpense: "Add Expense",
+ editExpense: "Edit Expense",
+
+ // More
+ more: "More",
+ expenseCategory: "Expense Categories",
+ addCategory: "Add Category",
+ editCategory: "Edit Category",
+
+ // Setting
+ settings: "Settings",
+ notifications: "Notification",
+ setting: {
+ company: "Company Information",
+ account: "Account Settings",
+ preferences: "Preferences",
+ LanguageAndCurrency: "Language & Currency",
+ endpoint: 'API Endpoint'
+ },
+
+ // Taxes
+ taxes: "Tax Types",
+ addTaxes: "Add Tax",
+ editTaxes: "Edit Tax",
+
+ // Item
+ addItem: "Add Item",
+ editItem: "Edit Item",
+ items: "Items",
+
+ // Report
+ reports: "Reports",
+ salesReport: "Sales Report",
+ profitAndLossReport: "Profit & Loss Report",
+ expensesReport: "Expenses Report",
+ taxesReport: "Taxes Report",
+
+ // Filter
+ filter: "Filters",
+
+ back: "Back",
+ },
+
+ tabNavigation: {
+ invoices: "Invoices",
+ customers: "Customers",
+ payments: "Payments",
+ expenses: "Expenses",
+ more: "More",
+ },
+
+ invoices: {
+
+ title: 'Select Invoice',
+
+ tabs: {
+ DUE: "Due",
+ DRAFT: "Draft",
+ ALL: "ALL"
+ },
+
+ invoiceDate: 'Invoice Date',
+ dueDate: 'Due Date',
+ discount: 'DISCOUNT',
+ invoiceNumber: "Invoice Number",
+ referenceNumber: "Reference Number",
+ template: "Template",
+ templatePlaceholder: "Select or Add a new Template",
+ customer: "Customer",
+ customerPlaceholder: "Select or Add new Customer",
+ items: "Items",
+ unit: "Unit",
+ addItem: "Add Item",
+ notes: "Notes",
+ notePlaceholder: "Additional notes visible on Invoice",
+ taxPlaceholder: "+ Add Tax",
+ subtotal: "SUBTOTAL",
+ totalAmount: "TOTAL AMOUNT",
+ status: "Status",
+ statusPlaceholder: "Select a Status",
+ paidStatus: "Paid Status",
+ paidStatusPlaceholder: "Select a Paid Status",
+ fromDate: "From Date",
+ toDate: "To Date",
+
+ alert: {
+ removeDescription: "You will not be able to recover this Invoice",
+ paymentAttachedTitle: "Action Failed",
+ paymentAttachedDescription: "Cannot delete an invoice with an existing payment.",
+ draftTitle: "Save changes?",
+ },
+
+ empty: {
+ due: {
+ title: "You have no due invoices",
+ description: "This section will contain the list of due invoices.",
+ },
+ draft: {
+ title: "You have no draft invoices",
+ description: "This section will contain the list of draft invoices.",
+ },
+ all: {
+ title: "You haven't created any invoices",
+ },
+
+ title: "No invoices yet!",
+ description: "This section will contain the list of invoices.",
+ buttonTitle: "Add New Invoice"
+ },
+
+ actions: {
+ downloadPdf: 'Download PDF',
+ sendInvoice: 'Send Invoice',
+ editInvoice: 'Edit Invoice',
+ delete: 'Delete',
+ recordPayment: "Record Payment",
+ markAsSent: "Mark As Sent"
+ }
+ },
+
+ customers: {
+ displayName: "Display name",
+ contactName: "Primary Contact Name",
+ email: "Email",
+ name: "Name",
+ phone: "Phone",
+ website: "Website",
+ currency: "Currency",
+ billingAddress: "Billing Address",
+ shippingAddress: "Shipping Address",
+ enablePortal: "Enable Portal",
+ password: "Password",
+
+ filterDisplayName: "Display Name",
+ filterContactName: "Contact Name",
+
+ alertEmailAlreadyInUse: "Email already in use",
+ alertDescription: "You will not be able to recover this Customer",
+
+ address: {
+ name: "Name",
+ country: "Country",
+ state: "State",
+ city: "City",
+ address: "Address",
+ street1: "Street1",
+ street2: "Street2",
+ phone: "Phone",
+ zipcode: "Zip Code",
+ sameAs: "Copy from Billing Address"
+ },
+
+ removeCustomer: "Delete",
+
+ empty: {
+ title: "No customers yet!",
+ description: "This section will contain the list of customers.",
+ buttonTitle: "Add New Customer",
+
+ country: { title: "No Country Available" },
+ state: { title: "No State Available" },
+ city: { title: "No City Available" }
+ },
+
+ title: 'Select a Customer',
+ placeholder: 'Select Customer',
+ },
+
+ payments: {
+
+ date: "Payment Date",
+ number: "Payment Number",
+ customer: "Customer",
+ customerPlaceholder: "Select or Add new Customer",
+ mode: "Payment Mode",
+ modePlaceholder: "Select Payment Mode",
+ amount: "Amount",
+ notes: "Notes",
+ notesPlaceholder: "Additional notes",
+ invoice: "Invoice",
+ invoicePlaceholder: "Select Invoice",
+
+ alertAmount: "The payment entered is more than the total amount due for this invoice.",
+ alertDescription: "You will not be able to recover this Payment",
+
+ removePayment: "Delete",
+
+ empty: {
+ title: "No payments yet!",
+ description: 'This section will contain the list of payments.',
+ buttonTitle: 'Add New Payment',
+ },
+ },
+
+ expenses: {
+ receipt: "Receipt",
+ date: "Expense Date",
+ fromDate: "From Date",
+ toDate: "To Date",
+ amount: "Amount",
+ category: "Category",
+ categoryPlaceholder: "Select Category",
+ notes: "Notes",
+ notesPlaceholder: "Additional notes",
+ viewReceipt: "View Receipt",
+
+ alertDescription: "You will not be able to recover this Expense",
+ removeExpense: "Delete",
+
+ noCategories: "No categories yet!",
+
+ empty: {
+ title: "No expenses yet!",
+ description: 'This section will contain the list of expenses.',
+ buttonTitle: 'Add New Expense',
+ },
+ },
+
+ items: {
+
+ title: 'Select Item',
+
+ name: "Item Name",
+ description: "Description (optional)",
+ quantity: "Quantity",
+ price: "Price",
+ subTotal: "SUBTOTAL",
+ discountType: "Discount Type",
+ discount: "Discount",
+ finalDiscount: "DISCOUNT",
+ finalAmount: "AMOUNT",
+ taxes: "Taxes",
+ selectTax: "Select or Add New Taxes",
+ unit: "Unit",
+ unitPlaceholder: "Select unit",
+
+ alertDescription: "You will not be able to recover this Item",
+ lessAmount: "Total Amount should't less than 0",
+
+ alreadyAttachTitle: "Action Failed",
+ alreadyAttachDescription: "Cannot delete an item which is already in use",
+
+ empty: {
+ title: "No items yet!",
+ description: 'This section will contain the list of items.',
+ buttonTitle: 'Add New Items',
+ },
+ },
+
+ more: {
+ estimate: "Estimates",
+ items: "Items",
+ reports: "Reports",
+ settings: "Settings",
+ logout: "Logout",
+ },
+
+ categories: {
+
+ title: "Category Name",
+ description: "Description",
+
+ alertDescription: "You will not be able to recover this Category",
+ alreadyUsed: "Category already in use",
+
+ empty: {
+ title: "No categories yet!",
+ description: "This section will contain the list of categories.",
+ buttonTitle: "Add New Category"
+ }
+ },
+
+ settings: {
+ accountSettings: "Account settings",
+ companyInformation: "Company Info",
+ preference: "Preferences",
+ LanguageAndCurrency: "Language & Currency",
+ notification: "Notifications",
+ taxes: "Tax Types",
+ expenseCategory: "Expense Categories",
+
+ endpoint: "API Endpoint",
+
+ company: {
+ name: "Company Name",
+ email: "Email",
+ phone: "Phone",
+ address: "Address",
+ street1: "Street1",
+ street2: "Street2",
+ zipcode: "Zip Code",
+ website: "Website",
+ logo: "Company Logo"
+ },
+
+ account: {
+ name: "Name",
+ email: "Email",
+ password: "Password",
+ confirmPassword: "Confirm Password"
+ },
+
+ notifications: {
+ send: "Send Notifications To",
+ invoiceViewed: "Invoice Viewed",
+ invoiceViewedUpdated: "Notification setting updated successfully",
+ invoiceViewedDescription: "When your customer views the invoice link sent via email.",
+
+ estimateViewed: "Estimate Viewed",
+ estimateViewedDescription: "When your customer views the estimate link sent via email.",
+ estimateViewedUpdated: "Notification setting updated successfully",
+ },
+
+ preferences: {
+ currency: "Currency",
+ currencyPlaceholder: "Select Currency",
+ language: "Language",
+ languagePlaceholder: "Select Language",
+ timeZone: "Time Zone",
+ timeZonePlaceholder: "Select Time Zone",
+ dateFormat: "Date Format",
+ dateFormatPlaceholder: "Select Date Format",
+ fiscalYear: "Financial Year",
+ fiscalYearPlaceholder: "Select Financial Year",
+ discountSetting: "Discount Setting",
+ discountPerItem: "Discount Per Item",
+ discountPerItemPlaceholder: "Enable this if you want to add Discount to individual invoice items. By default, Discount are added directly to the invoice.",
+ taxPerItem: "Tax Per Item",
+ taxPerItemPlaceholder: "Enable this if you want to add Tax to individual invoice items. By default, Tax are added directly to the invoice.",
+
+ settingUpdate: "Setting updated successfully"
+ }
+ },
+
+ login: {
+ email: "Enter Your Email",
+ password: "Enter Your Password",
+ invalid: "Invalid Credentials"
+ },
+ logout: {
+ confirmation: "Are you sure you want to logout?",
+ title: "Logout"
+ },
+ forgot: {
+ emailLabel: "Enter your email and we will send you reset password link",
+ emailPlaceholder: "Enter Your Email",
+ emailSendTitle: "Check Your Email",
+ emailSendDescription: " A Password reset link has been sent to your email, Please follow the instructions on the email to create a new password",
+ emailSendError: "Email could not be sent to this email address.",
+ },
+
+ lostInternet: {
+ title: "Connection Lost",
+ description: "Please check your mobile or Wifi settings to verify your internet connectivity and then try again."
+ },
+
+ updateApp: {
+ title: "App Version Expired!",
+ description: "This version of the app is out of date. Please install the latest update to keep using Crater."
+ },
+
+ endpoint: {
+ endpointURL: 'Endpoint URL',
+ endpointDesc: 'Above URL will be used to connect with your self-hosted installation of crater.',
+ urlPlaceHolder: 'Eg: https://craterapp.com',
+ alertInvalidUrl: "Invalid URL",
+ },
+
+ estimates: {
+
+ tabs: {
+ SENT: "Sent",
+ DRAFT: "Draft",
+ ALL: "All"
+ },
+
+ estimateDate: 'Estimate Date',
+ expiryDate: 'Expiry Date',
+ discount: 'DISCOUNT',
+ estimateNumber: "Estimate Number",
+ template: "Template",
+ templatePlaceholder: "Select or Add a new Template",
+ customer: "Customer",
+ customerPlaceholder: "Select or Add new Customer",
+ items: "Items",
+ unit: "Unit",
+ taxPlaceholder: "+ Add Tax",
+ addItem: "Add Item",
+ notes: "Notes",
+ notePlaceholder: "Additional notes visible on estimate",
+
+ fromDate: "From Date",
+ toDate: "To Date",
+ status: "Status",
+ statusPlaceholder: "Select a Status",
+
+ subtotal: "SUBTOTAL",
+ tax: "TAX",
+ totalAmount: "TOTAL AMOUNT",
+
+ alert: {
+ convertToInvoiceDescription: "Are you sure you want to convert this Estimate into an Invoice?",
+ removeDescription: "You will not be able to recover this Estimate",
+ lessAmount: "Total Amount should't less than 0",
+ draftTitle: "Save changes?",
+ },
+
+ empty: {
+ sent: {
+ title: "You have no sent estimates",
+ description: "This section will contain the list of sent estimates.",
+ },
+ draft: {
+ title: "You have no draft estimates",
+ description: "This section will contain the list of draft estimates.",
+ },
+ all: {
+ title: "You haven't created any estimates",
+ description: "This section will contain the list of estimates.",
+ },
+
+ buttonTitle: "Add New Estimate"
+ },
+
+ actions: {
+ downloadPdf: 'Download PDF',
+ sendEstimate: 'Send Estimate',
+ editEstimate: 'Edit Estimate',
+ delete: 'Delete',
+ convertToInvoice: 'Convert To Invoice',
+ markAsSent: 'Mark As Sent',
+ markAsAccepted: 'Mark As Accepted',
+ markAsRejected: 'Mark As Rejected',
+ }
+ },
+
+ currencies: {
+ title: 'Select Currency',
+ empty: {
+ title: "No currencies yet!",
+ }
+ },
+
+ languages: {
+ title: 'Select Language',
+ empty: {
+ title: "No languages yet!",
+ description: "This section will contain the list of Languages.",
+ }
+ },
+
+ timeZones: {
+ title: 'Select TimeZone',
+ empty: {
+ title: "No TimeZones yet!",
+ description: "This section will contain the list of Timezones.",
+ }
+ },
+
+ dateFormats: {
+ title: 'Select Date Format',
+ empty: {
+ title: "No date formats yet!",
+ description: "This section will contain the list of Date Formats.",
+ }
+ },
+
+ fiscalYears: {
+ title: 'Select financial year',
+ empty: {
+ title: "No Financial Years yet!",
+ description: "This section will contain the list of Financial Years.",
+ }
+ },
+
+ reports: {
+ sales: 'Sales',
+ profitAndLoss: 'Profit & Loss',
+ expenses: 'Expenses',
+ taxes: 'Taxes',
+ dateRange: 'Select Date Range',
+ fromDate: 'From Date',
+ toDate: 'To Date',
+ reportType: 'Report Type',
+
+ byCustomer: 'By Customer',
+ byItem: 'By Item',
+
+ today: 'Today',
+ thisWeek: 'This Week',
+ thisMonth: 'This Month',
+ thisQuarter: 'This Quarter',
+ thisYear: 'This Year',
+ currentFiscalQuarter: 'Current Fiscal Quarter',
+ currentFiscalYear: 'Current Fiscal Year',
+ previousWeek: 'Previous Week',
+ previousMonth: 'Previous Month',
+ previousQuarter: 'Previous Quarter',
+ previousYear: 'Previous Year',
+ previousFiscalQuarter: 'Previous Fiscal Quarter',
+ previousFiscalYear: 'Previous Fiscal Year',
+ custom: 'Custom',
+
+ },
+
+ taxes: {
+
+ title: 'Taxes',
+
+ type: "Tax Name",
+ description: "Description",
+ percentage: "Tax Percentage",
+ compoundTax: "Compound Tax",
+
+ alertDescription: "You will not be able to recover this Tax",
+ alreadyUsed: "Tax already in use",
+
+ empty: {
+ title: "No Tax Types yet!",
+ description: 'This section will contain the list of Tax Types.',
+ buttonTitle: 'Add Tax Type',
+ },
+ },
+
+
+ filePicker: {
+ file: "Click here to choose a file",
+ permission: "Sorry, we need camera roll permissions to make this work!"
+ },
+
+ alert: {
+ title: "Are you sure?",
+ action: {
+ discard: "Discard",
+ saveAsDraft: "Save As Draft"
+ }
+ },
+
+ button: {
+
+ remove: "Remove",
+ save: "Save",
+ edit: "Edit",
+ retry: "Retry",
+ done: "Done",
+ skip: "Skip",
+ clear: "Clear",
+
+ // Invoices
+ viewPdf: "View PDF",
+
+ // Auth
+ singIn: "Sign in",
+ singInGoogle: "Sign in with google",
+ forget: "Forget Password?",
+
+ update: "Update",
+ updateCapital: "UPDATE",
+ chooseTemplate: "Choose Template",
+
+ // Reports
+ generateReport: " Generate Report",
+
+ recoveryEmail: "Send reset link",
+ recoveryEmailAgain: "Send Again",
+ },
+
+ filter: {
+ empty: {
+ filterTitle: "No Results found"
+ }
+ },
+
+ search: {
+ title: "Search",
+ noResult: 'No results for "{{search}}"',
+ noSearchResult: 'No results for',
+ },
+
+ validation: {
+ Customer: "Customer",
+ Invoice: "Invoice",
+ required: "Required field is empty",
+ field: "{{hint}} field is required",
+ choose: "Choose at least one item",
+ email: "Please enter a valid email address",
+ passwordCompare: "Passwords do not match",
+ minimumNumber: "must be greater than 0",
+ maximumNumber: "must be less than {{maxNumber}}",
+ numeric: "must be numeric",
+ moreThanDue: "{{hint}} Should not be more than Due Amount.",
+ url: "Invalid url (eg: https://craterapp.com)"
+ },
+
+};
\ No newline at end of file
diff --git a/src/api/lang/es.js b/src/api/lang/es.js
new file mode 100644
index 00000000..67789157
--- /dev/null
+++ b/src/api/lang/es.js
@@ -0,0 +1,633 @@
+export const es = {
+
+ header: {
+
+ // Invoices
+ invoices: "Facturas",
+ addInvoice: "Agregar factura",
+ editInvoice: "Editar factura",
+
+ // Estimates
+ estimates: "Estimaciones",
+ addEstimate: "Añadir presupuesto",
+ editEstimate: "Editar estimación",
+
+ // Templates
+ template: "Selecciona una plantilla",
+
+ // Customers
+ customers: "Clientes",
+ addCustomer: "Agregar cliente",
+ editCustomer: "Editar cliente",
+
+ selectCurrency: "Seleccione el tipo de moneda",
+ billingAddress: "Dirección de Envio",
+ shippingAddress: "Dirección de EnvÃo",
+
+ country: "Seleccionar paÃs",
+ state: "Seleccione estado",
+ city: "Ciudad selecta",
+
+ // Payments
+ payments: "Pagos",
+ addPayment: "Agregar pago",
+ editPayment: "Editar pago",
+
+
+ // Expenses
+ expenses: "Gastos",
+ addExpense: "Añadir gastos",
+ editExpense: "Editar gasto",
+
+ // More
+ more: "Más",
+ expenseCategory: "CategorÃas de gastos",
+ addCategory: "añadir categorÃa",
+ editCategory: "Editar categoria",
+
+ // Setting
+ settings: "Configuraciones",
+ notifications: "Notificación",
+ setting: {
+ company: "empresa",
+ account: "configuración de cuenta",
+ preferences: "Preferencias",
+ LanguageAndCurrency: "Idioma y moneda",
+ endpoint: "Punto final de API"
+ },
+
+ // Taxes
+ taxes: "Tipos de impuestos",
+ addTaxes: "Agregar impuesto",
+ editTaxes: "Editar impuesto",
+
+ // Item
+ addItem: "Añadir artÃculo",
+ editItem: "Editar elemento",
+ items: "ArtÃculos",
+
+ // Report
+ reports: "Informes",
+ salesReport: "Reporte de ventas",
+ profitAndLossReport: "Informe de pérdidas y ganancias",
+ expensesReport: "Informe de gastos",
+ taxesReport: "Informe de impuestos",
+
+ // Filter
+ filter: "Filtros",
+
+ back: "atrás",
+ },
+
+ tabNavigation: {
+ invoices: "Facturas",
+ customers: "Clientes",
+ payments: "pagos",
+ expenses: "Gastos",
+ more: "Más",
+ },
+
+ invoices: {
+
+ title: "Seleccionar factura",
+
+ tabs: {
+ DUE: "Debido",
+ DRAFT: "SequÃa",
+ ALL: "Todas"
+ },
+
+ invoiceDate: "Fecha",
+ dueDate: "Fecha debida",
+ discount: "DESCUENTO",
+ invoiceNumber: "Numero de factura",
+ referenceNumber: "Número de referencia",
+ template: "Modelo",
+ templatePlaceholder: "Seleccionar o agregar una nueva plantilla",
+ customer: "Cliente",
+ customerPlaceholder: "Seleccionar cliente",
+ items: "ArtÃculos",
+ unit: "Unidad",
+ addItem: "Añadir artÃculo",
+ notes: "Notas",
+ notePlaceholder: "Notas adicionales visibles en la factura",
+ taxPlaceholder: "+ Agregar impuesto",
+ subtotal: "TOTAL PARCIAL",
+ totalAmount: "CANTIDAD TOTAL",
+ status: "Estado",
+ statusPlaceholder: "Seleccione un estado",
+ paidStatus: "Estado pagado",
+ paidStatusPlaceholder: "Seleccionar estado pagado",
+ fromDate: "Partir de la fecha",
+ toDate: "Hasta la fecha",
+
+ alert: {
+ removeDescription: "No podrá recuperar esta factura",
+ paymentAttachedTitle: "Accion: Fallida",
+ paymentAttachedDescription: "No se puede eliminar una factura con un pago existente.",
+ draftTitle: "¿Quieres ahorrar?",
+ },
+
+ empty: {
+ due: {
+ title: "No tiene facturas vencidas",
+ description: "Esta sección contendrá la lista de facturas vencidas.",
+ },
+ draft: {
+ title: "No tienes facturas en borrador",
+ description: "Esta sección contendrá la lista de proyectos de facturas.",
+ },
+ all: {
+ title: "No ha creado ninguna factura.",
+ },
+
+ title: "Aún no hay facturas!",
+ description: "Esta sección contendrá la lista de facturas.",
+ buttonTitle: "Añadir nueva factura"
+ },
+
+ actions: {
+ downloadPdf: "Descargar PDF",
+ sendInvoice: "Enviará la factura",
+ editInvoice: "Editar factura",
+ delete: "Eliminar",
+ recordPayment: "Registro de pago",
+ markAsSent: "Marcar como enviado",
+ }
+ },
+
+ customers: {
+ displayName: "Nombre para mostrar",
+ contactName: "Nombre de contacto primario",
+ email: "Email",
+ name: "Nombre",
+ phone: "Teléfono",
+ website: "Sitio web",
+ currency: "Moneda",
+ billingAddress: "Dirección de Envio",
+ shippingAddress: "Dirección de EnvÃo",
+ enablePortal: "Habilitar portal",
+ password: "Contraseña",
+
+ filterDisplayName: "Nombre para mostrar",
+ filterContactName: "Nombre de contacto",
+
+ alertEmailAlreadyInUse: "Correo electrónico ya en uso",
+ alertDescription: "No podrá recuperar a este cliente",
+
+ address: {
+ name: "Nombre",
+ country: "PaÃs",
+ state: "Estado",
+ city: "Ciudad",
+ address: "Habla a",
+ street1: "Calle1",
+ street2: "Calle2",
+ phone: "Teléfono",
+ zipcode: "Código postal",
+ sameAs: "Copiar de facturación"
+ },
+
+ removeCustomer: "Eliminar",
+
+ empty: {
+ title: "Aún no hay clientes!",
+ description: "Esta sección contendrá la lista de clientes.",
+ buttonTitle: "Agregar nuevo cliente",
+
+ country: { title: "Ningún paÃs disponible" },
+ state: { title: "No hay estado disponible" },
+ city: { title: "No hay ciudad disponible" }
+ },
+
+ title: "Seleccione un cliente",
+ placeholder: "Seleccionar cliente",
+ },
+
+ payments: {
+
+ date: "Fecha",
+ number: "Pago no",
+ customer: "Cliente",
+ customerPlaceholder: "Seleccionar cliente",
+ mode: "Modo de pago",
+ modePlaceholder: "Seleccionar modo",
+ amount: "Cantidad",
+ notes: "Notas",
+ notesPlaceholder: "Notas adicionales",
+ invoice: "Factura",
+ invoicePlaceholder: "Seleccionar factura",
+
+ alertAmount: "El pago ingresado es mayor que el monto total adeudado por esta factura.",
+ alertDescription: "No podrá recuperar este pago",
+
+ removePayment: "Eliminar",
+
+ empty: {
+ title: "Aún no hay pagos!",
+ description: "Esta sección contendrá la lista de pagos.",
+ buttonTitle: "Agregar nuevo pago",
+ },
+ },
+
+ expenses: {
+ receipt: "Recibo",
+ date: "Fecha de gastos",
+ fromDate: "Partir de la fecha",
+ toDate: "Hasta la fecha",
+ amount: "Cantidad",
+ category: "CategorÃa",
+ categoryPlaceholder: "selecciona una categorÃa",
+ notes: "Notas",
+ notesPlaceholder: "Notas adicionales",
+ viewReceipt: "Ver recibo",
+
+ alertDescription: "No podrá recuperar este gasto",
+ removeExpense: "Eliminar",
+
+ noCategories: "Aún no hay categorÃas!",
+
+ empty: {
+ title: "Aún no hay gastos!",
+ description: "Esta sección contendrá la lista de gastos.",
+ buttonTitle: "Añadir nuevo gasto",
+ },
+ },
+
+ items: {
+
+ title: "Seleccione un artÃculo",
+
+ name: "Nombre del árticulo",
+ description: "Descripción (opcional)",
+ quantity: "Cantidad",
+ price: "Precio",
+ subTotal: "TOTAL PARCIAL",
+ discountType: "Tipo de descuento",
+ discount: "Descuento",
+ finalDiscount: "DESCUENTO",
+ finalAmount: "CANTIDAD",
+ taxes: "Impuestos",
+ selectTax: "Seleccionar impuestos",
+ unit: "Unidad",
+ unitPlaceholder: "Seleccionar unidad",
+
+ alertDescription: "No podrás recuperar este artÃculo",
+ lessAmount: "La cantidad total no debe ser inferior a 0",
+
+ alreadyAttachTitle: "Accion: Fallida",
+ alreadyAttachDescription: "No se puede eliminar un elemento que ya está en uso.",
+
+ empty: {
+ title: "Aún no hay artÃculos!",
+ description: "Esta sección contendrá la lista de artÃculos.",
+ buttonTitle: "Agregar nuevos elementos",
+ },
+ },
+
+ more: {
+ estimate: "Estimados",
+ items: "ArtÃculos",
+ reports: "Informes",
+ settings: "Configuraciones",
+ logout: "Cerrar sesión",
+ },
+
+ categories: {
+
+ title: "nombre de la categorÃa",
+ description: "Descripción",
+
+ alertDescription: "No podrás recuperar esta categorÃa",
+ alreadyUsed: "CategorÃa ya en uso",
+
+ empty: {
+ title: "Aún no hay categorÃas!",
+ description: "Esta sección contendrá la lista de categorÃas.",
+ buttonTitle: "Añadir nueva categoria"
+ }
+ },
+
+ settings: {
+ accountSettings: "configuración de cuenta",
+ companyInformation: "Empresa",
+ preference: "Preferencias",
+ LanguageAndCurrency: "Idioma y moneda",
+ notification: "Notificaciones",
+ taxes: "Tipos de impuestos",
+ expenseCategory: "CategorÃas de gastos",
+
+ endpoint: "point final api",
+
+ company: {
+ name: "nombre de empresa",
+ email: "Email",
+ phone: "Teléfono",
+ address: "Habla a",
+ street1: "Calle 1",
+ street2: "Calle 2",
+ zipcode: "Código postal",
+ website: "Sitio web",
+ logo: "Logo de la compañÃa"
+ },
+
+ account: {
+ name: "Nombre",
+ email: "Email",
+ password: "Contraseña",
+ confirmPassword: "Confirmar contraseña"
+ },
+
+ notifications: {
+ send: "Enviar notificaciones a",
+ invoiceViewed: "Factura vista",
+ invoiceViewedUpdated: "Configuración de notificaciones actualizada correctamente",
+ invoiceViewedDescription: "Cuando su cliente ve el enlace de la factura enviado por correo electrónico.",
+
+ estimateViewed: "Estimación vista",
+ estimateViewedDescription: "Cuando su cliente ve el enlace estimado enviado por correo electrónico.",
+ estimateViewedUpdated: "Configuración de notificaciones actualizada correctamente",
+ },
+
+ preferences: {
+ currency: "Moneda",
+ currencyPlaceholder: "Seleccione el tipo de moneda",
+ language: "Idioma",
+ languagePlaceholder: "Seleccione el idioma",
+ timeZone: "Zona horaria",
+ timeZonePlaceholder: "Selecciona la zona horaria",
+ dateFormat: "Formato de fecha",
+ dateFormatPlaceholder: "Seleccionar formato de fecha",
+ fiscalYear: "Año financiero",
+ fiscalYearPlaceholder: "Seleccionar año financiero",
+ discountSetting: "Ajuste de descuento",
+ discountPerItem: "Descuento por artÃculo",
+ discountPerItemPlaceholder: "HabilÃtelo si desea agregar Descuento a artÃculos de factura individuales. Por defecto, los descuentos se agregan directamente a la factura.",
+ taxPerItem: "Impuesto por artÃculo",
+ taxPerItemPlaceholder: "HabilÃtelo si desea agregar Impuestos a artÃculos de factura individuales. Por defecto, los impuestos se agregan directamente a la factura.",
+
+ settingUpdate: "Configuración actualizada con éxito"
+ }
+ },
+
+ login: {
+ email: "Introduce tu correo electrónico",
+ password: "Ingresa tu contraseña",
+ invalid: "Credenciales no válidas"
+ },
+ logout: {
+ confirmation: "¿Estás seguro de que quieres cerrar sesión?",
+ title: "Cerrar sesión"
+ },
+ forgot: {
+ emailLabel: "Ingrese su correo electrónico y le enviaremos un enlace para restablecer la contraseña",
+ emailPlaceholder: "Introduce tu correo electrónico",
+ emailSendTitle: "Consultar su correo electrónico",
+ emailSendDescription: " Se ha enviado un enlace de restablecimiento de contraseña a su correo electrónico. Siga las instrucciones en el correo electrónico para crear una nueva contraseña",
+ emailSendError: "No se pudo enviar el correo electrónico a esta dirección de correo electrónico.",
+ },
+
+ lostInternet: {
+ title: "Conexión perdida",
+ description: "Verifique la configuración de su dispositivo móvil o Wifi para verificar su conectividad a Internet e intente nuevamente."
+ },
+
+ updateApp: {
+ title: "¡Versión de la aplicación caducada!",
+ description: "Esta versión de la aplicación está desactualizada. Instala la última actualización para seguir usando Crater."
+ },
+
+ endpoint: {
+ endpointURL: "URL de punto final",
+ endpointDesc: "La URL anterior se usará para conectarse con su instalación de cráter autohospedada.",
+ urlPlaceHolder: "p.ej: https://craterapp.com",
+ alertInvalidUrl: "URL invalida",
+ },
+
+ estimates: {
+
+ tabs: {
+ SENT: "Expedido",
+ DRAFT: "SequÃa",
+ ALL: "Todas"
+ },
+
+ estimateDate: "Fecha",
+ expiryDate: "Fecha Expiración",
+ discount: "DESCUENTO",
+ estimateNumber: "Numero Estimado",
+ template: "Modelo",
+ templatePlaceholder: "Seleccionar o agregar una nueva plantilla",
+ customer: "Cliente",
+ customerPlaceholder: "Seleccionar cliente",
+ items: "ArtÃculos",
+ unit: "Unidad",
+ taxPlaceholder: "+ Agregar impuesto",
+ addItem: "Añadir artÃculo",
+ notes: "Notas",
+ notePlaceholder: "Notas adicionales visibles en la estimación",
+
+ fromDate: "Partir de la fecha",
+ toDate: "Hasta la fecha",
+ status: "Estado",
+ statusPlaceholder: "Seleccione un estado",
+
+ subtotal: "TOTAL PARCIAL",
+ tax: "IMPUESTO",
+ totalAmount: "CANTIDAD TOTAL",
+
+ alert: {
+ convertToInvoiceDescription: "¿Está seguro de que desea convertir esta estimación en una factura?",
+ removeDescription: "No podrá recuperar esta estimación",
+ lessAmount: "La cantidad total no debe ser inferior a 0",
+ draftTitle: "¿Guardar cambios?",
+ },
+
+ empty: {
+ sent: {
+ title: "No tienes estimaciones enviadas",
+ description: "Esta sección contendrá la lista de estimaciones enviadas.",
+ },
+ draft: {
+ title: "No tienes borradores de estimaciones",
+ description: "Esta sección contendrá la lista de proyectos de estimaciones.",
+ },
+ all: {
+ title: "No has creado ninguna estimación.",
+ description: "Esta sección contendrá la lista de estimaciones.",
+ },
+
+ buttonTitle: "Añadir nuevo presupuesto"
+ },
+
+ actions: {
+ downloadPdf: "Descargar PDF",
+ sendEstimate: "Enviar presupuesto",
+ editEstimate: "Editar estimación",
+ delete: "Eliminar",
+ convertToInvoice: "Convertir a factura",
+ markAsSent: "Marcar como enviado",
+ markAsAccepted: "Marcar como aceptado",
+ markAsRejected: "Marcar como rechazado",
+ }
+ },
+
+ currencies: {
+ title: "Seleccione el tipo de moneda",
+ empty: {
+ title: "¡Aún no hay monedas!",
+ }
+ },
+
+ languages: {
+ title: "Seleccione el idioma",
+ empty: {
+ title: "¡Aún no hay idiomas!",
+ description: "Esta sección contendrá la lista de idiomas.",
+ }
+ },
+
+ timeZones: {
+ title: "Selecciona la zona horaria",
+ empty: {
+ title: "No hay zonas horarias todavÃa!",
+ description: "Esta sección contendrá la lista de zonas horarias.",
+ }
+ },
+
+ dateFormats: {
+ title: "Seleccionar formato de fecha",
+ empty: {
+ title: "¡Aún no hay formatos de fecha!",
+ description: "Esta sección contendrá la lista de formatos de fecha.",
+ }
+ },
+
+ fiscalYears: {
+ title: "Seleccionar año financiero",
+ empty: {
+ title: "No hay años financieros todavÃa!",
+ description: "Esta sección contendrá la lista de ejercicios financieros.",
+ }
+ },
+
+ reports: {
+ sales: "Ventas",
+ profitAndLoss: "Pérdida de beneficios",
+ expenses: "Gastos",
+ taxes: "Impuestos",
+ dateRange: "Seleccionar rango de fechas",
+ fromDate: "Partir de la fecha",
+ toDate: "Hasta la fecha",
+ reportType: "Tipo de informe",
+
+ byCustomer: "Por el cliente",
+ byItem: "Por artÃculo",
+
+ today: "Hoy",
+ thisWeek: "Esta semana",
+ thisMonth: "Este mes",
+ thisQuarter: "Este cuarto",
+ thisYear: "Este año",
+ currentFiscalQuarter: "Trimestre fiscal actual",
+ currentFiscalYear: "Año fiscal actual",
+ previousWeek: "Semana pasada",
+ previousMonth: "Mes anterior",
+ previousQuarter: "Trimestre anterior",
+ previousYear: "Año anterior",
+ previousFiscalQuarter: "Trimestre fiscal anterior",
+ previousFiscalYear: "Año fiscal anterior",
+ custom: "Personalizado",
+
+ },
+
+ taxes: {
+
+ title: "Impuestos",
+
+ type: "Nombre fiscal",
+ description: "Descripción",
+ percentage: "Porcentaje fiscal",
+ compoundTax: "Impuesto compuesto",
+
+ alertDescription: "No podrá recuperar este impuesto",
+ alreadyUsed: "Impuesto ya en uso",
+
+ empty: {
+ title: "No hay tipos de impuestos todavÃa!",
+ description: "Esta sección contendrá la lista de tipos de impuestos.",
+ buttonTitle: "Agregar tipo de impuesto",
+ },
+ },
+
+
+ filePicker: {
+ file: "Haga clic aquà para elegir un archivo.",
+ permission: "Lo sentimos, necesitamos permisos de cámara para hacer que esto funcione."
+ },
+
+ alert: {
+ title: "¿Estás seguro?",
+ action: {
+ discard: "Descarte",
+ saveAsDraft: "Guardar como borrador"
+ }
+ },
+
+ button: {
+
+ remove: "Eliminar",
+ save: "Salvar",
+ edit: "Editar",
+ retry: "Procesar de nuevo",
+ done: "Hecho",
+ skip: "Omitir",
+ clear: "Claro",
+
+ // Invoices
+ viewPdf: "Ver PDF",
+
+ // Auth
+ singIn: "Registrarse",
+ singInGoogle: "Inicia sesión con Google",
+ forget: "¿Contraseña olvidada?",
+
+ update: "Actualizar",
+ updateCapital: "ACTUALIZAR",
+ chooseTemplate: "Elegir la plantilla",
+
+ // Reports
+ generateReport: "Generar informe",
+
+ recoveryEmail: "Enviar enlace de reinicio",
+ recoveryEmailAgain: "Enviar de nuevo",
+ },
+
+ filter: {
+ empty: {
+ filterTitle: "No se han encontrado resultados"
+ }
+ },
+
+ search: {
+ title: "Buscar",
+ noResult: 'No hay resultados para "{{search}}"',
+ noSearchResult: 'No hay resultados para',
+ },
+
+ validation: {
+ Customer: "Cliente",
+ Invoice: "Factura",
+ required: "El campo obligatorio está vacÃo.",
+ field: "{{hint}} Se requiere campo",
+ choose: "Elige al menos un artÃculo",
+ email: "Por favor, introduce una dirección de correo electrónico válida",
+ passwordCompare: "Las contraseñas no coinciden",
+ minimumNumber: "debe ser mayor que 0",
+ maximumNumber: "debe ser menor que {{maxNumber}}",
+ numeric: "debe ser numérico",
+ moreThanDue: "{{hint}} No debe ser más que el monto adeudado.",
+ url: "URL invalida (p.ej: https://craterapp.com)"
+ },
+
+};
\ No newline at end of file
diff --git a/src/api/lang/fr.js b/src/api/lang/fr.js
new file mode 100644
index 00000000..536ae9c2
--- /dev/null
+++ b/src/api/lang/fr.js
@@ -0,0 +1,633 @@
+export const fr = {
+
+ header: {
+
+ // Invoices
+ invoices: "Factures",
+ addInvoice: "Ajouter une facture",
+ editInvoice: "Modifier la facture",
+
+ // Estimates
+ estimates: "Estimations",
+ addEstimate: "Ajouter une estimation",
+ editEstimate: "Modifier l'estimation",
+
+ // Templates
+ template: "Sélectionnez un modèle",
+
+ // Customers
+ customers: "Les clients",
+ addCustomer: "Ajouter un client",
+ editCustomer: "Modifier le client",
+
+ selectCurrency: "Sélectionnez la devise",
+ billingAddress: "Adresse de facturation",
+ shippingAddress: "Adresse de livraison",
+
+ country: "Choisissez le pays",
+ state: "Sélectionnez l'état",
+ city: "Sélectionnez une ville",
+
+ // Payments
+ payments: "Paiements",
+ addPayment: "Ajouter un paiement",
+ editPayment: "Modifier le paiement",
+
+
+ // Expenses
+ expenses: "les frais",
+ addExpense: "Ajouter une dépense",
+ editExpense: "Modifier les dépenses",
+
+ // More
+ more: "Plus",
+ expenseCategory: "Catégories de dépenses",
+ addCategory: "ajouter une catégorie",
+ editCategory: "Modifier la catégorie",
+
+ // Setting
+ settings: "Paramètres",
+ notifications: "Notification",
+ setting: {
+ company: "compagnie",
+ account: "réglage du compte",
+ preferences: "Préférences",
+ LanguageAndCurrency: "Langue et devise",
+ endpoint: "Point de terminaison de l'API"
+ },
+
+ // Taxes
+ taxes: "Types de taxe",
+ addTaxes: "Ajouter une taxe",
+ editTaxes: "Modifier la taxe",
+
+ // Item
+ addItem: "Ajouter un item",
+ editItem: "Modifier l'article",
+ items: "Articles",
+
+ // Report
+ reports: "Rapports",
+ salesReport: "Rapport des ventes",
+ profitAndLossReport: "Compte de résultat",
+ expensesReport: "Rapport de dépenses",
+ taxesReport: "Rapport de taxes",
+
+ // Filter
+ filter: "Les filtres",
+
+ back: "Retour",
+ },
+
+ tabNavigation: {
+ invoices: "Factures",
+ customers: "Les clients",
+ payments: "Paiements",
+ expenses: "les frais",
+ more: "Plus",
+ },
+
+ invoices: {
+
+ title: 'Sélectionnez facture',
+
+ tabs: {
+ DUE: "Dû",
+ DRAFT: "Brouillon",
+ ALL: "Toute"
+ },
+
+ invoiceDate: 'Date',
+ dueDate: "Date d'échéance",
+ discount: 'REMISE',
+ invoiceNumber: "Numéro de facture",
+ referenceNumber: "Numéro de réference",
+ template: "Modèle",
+ templatePlaceholder: "Sélectionner ou ajouter un nouveau modèle",
+ customer: "Client",
+ customerPlaceholder: "Sélectionner un client",
+ items: "Des articles",
+ unit: "Unité",
+ addItem: "Ajouter un item",
+ notes: "Remarques",
+ notePlaceholder: "Notes supplémentaires visibles sur la facture",
+ taxPlaceholder: "+ Ajouter une taxe",
+ subtotal: "TOTAL",
+ totalAmount: "MONTANT TOTAL",
+ status: "Statut",
+ statusPlaceholder: "Sélectionnez un statut",
+ paidStatus: "Statut payé",
+ paidStatusPlaceholder: "Sélectionnez le statut payé",
+ fromDate: "Partir de la date",
+ toDate: "À ce jour",
+
+ alert: {
+ removeDescription: "Vous ne pourrez pas récupérer cette facture",
+ paymentAttachedTitle: "Action: échoué",
+ paymentAttachedDescription: "Impossible de supprimer une facture avec un paiement existant.",
+ draftTitle: "Sauvegarder les modifications?",
+ },
+
+ empty: {
+ due: {
+ title: "Vous n'avez aucune facture due",
+ description: "Cette section contient la liste des factures dues.",
+ },
+ draft: {
+ title: "Vous n'avez pas de facture préliminaire",
+ description: "Cette section contiendra la liste des projets de factures.",
+ },
+ all: {
+ title: "Vous n'avez créé aucune facture",
+ },
+
+ title: "Aucune facture pour le moment!",
+ description: "Cette section contiendra la liste des factures.",
+ buttonTitle: "Ajouter une nouvelle facture"
+ },
+
+ actions: {
+ downloadPdf: 'Télécharger le PDF',
+ sendInvoice: 'Envoyer une facture',
+ editInvoice: 'Modifier la facture',
+ delete: 'Effacer',
+ recordPayment: "Record de paiement",
+ markAsSent: 'Marquer comme envoyé',
+ }
+ },
+
+ customers: {
+ displayName: "Afficher un nom",
+ contactName: "Nom du contact principal",
+ email: "Email",
+ name: "Nom",
+ phone: "Téléphone",
+ website: "Site Internet",
+ currency: "Devise",
+ billingAddress: "Adresse de facturation",
+ shippingAddress: "Adresse de livraison",
+ enablePortal: "Activer le portail",
+ password: "Mot de passe",
+
+ filterDisplayName: "Afficher un nom",
+ filterContactName: "Nom du contact",
+
+ alertEmailAlreadyInUse: "Email déjà utilisé",
+ alertDescription: "Vous ne pourrez pas récupérer ce client",
+
+ address: {
+ name: "Nom",
+ country: "Pays",
+ state: "Etat",
+ city: "Ville",
+ address: "Adresse",
+ street1: "Rue 1",
+ street2: "Rue 2",
+ phone: "Téléphone",
+ zipcode: "Code postal",
+ sameAs: "Copier de la facturation"
+ },
+
+ removeCustomer: "Effacer",
+
+ empty: {
+ title: "Pas encore de clients!",
+ description: "Cette section contiendra la liste des clients.",
+ buttonTitle: "Ajouter un nouveau client",
+
+ country: { title: "Pas de pays disponible" },
+ state: { title: "Aucun état disponible" },
+ city: { title: "Aucune ville disponible" }
+ },
+
+ title: 'Sélectionnez un client',
+ placeholder: 'Sélectionner un client',
+ },
+
+ payments: {
+
+ date: "Date",
+ number: "No de paiement",
+ customer: "Cliente",
+ customerPlaceholder: "Sélectionner un client",
+ mode: "Mode de paiement",
+ modePlaceholder: "Sélectionnez le mode",
+ amount: "Montant",
+ notes: "Remarques",
+ notesPlaceholder: "Notes complémentaires",
+ invoice: "Facture d'achat",
+ invoicePlaceholder: "Sélectionnez facture",
+
+ alertAmount: "Le paiement entré est supérieur au montant total dû pour cette facture.",
+ alertDescription: "Vous ne pourrez pas récupérer ce paiement",
+
+ removePayment: "Effacer",
+
+ empty: {
+ title: "Aucun paiement pour le moment!",
+ description: 'Cette section contiendra la liste des paiements.',
+ buttonTitle: 'Ajouter un nouveau paiement',
+ },
+ },
+
+ expenses: {
+ receipt: "Le reçu",
+ date: "Date de dépense",
+ fromDate: "Partir de la date",
+ toDate: "À ce jour",
+ amount: "Montant",
+ category: "Catégorie",
+ categoryPlaceholder: "Choisir une catégorie",
+ notes: "Remarques",
+ notesPlaceholder: "Notes complémentaires",
+ viewReceipt: "Voir le reçu",
+
+ alertDescription: "Vous ne pourrez pas récupérer cette dépense",
+ removeExpense: "Effacer",
+
+ noCategories: "Aucune catégorie pour le moment!",
+
+ empty: {
+ title: "Pas de dépenses pour le moment!",
+ description: 'Cette section contiendra la liste des dépenses.',
+ buttonTitle: 'Ajouter une nouvelle dépense',
+ },
+ },
+
+ items: {
+
+ title: 'Sélectionner un article',
+
+ name: "Nom de l'article",
+ description: "description (facultatif)",
+ quantity: "Quantité",
+ price: "Prix",
+ subTotal: "TOTAL",
+ discountType: "Type de remise",
+ discount: "Remise",
+ finalDiscount: "REMISE",
+ finalAmount: "MONTANT",
+ taxes: "Les taxes",
+ selectTax: "Sélectionnez les taxes",
+ unit: "Unité",
+ unitPlaceholder: "Sélectionnez l'unité",
+
+ alertDescription: "Vous ne pourrez pas récupérer cet article",
+ lessAmount: "Le montant total ne devrait pas être inférieur à 0",
+
+ alreadyAttachTitle: "Action: échoué",
+ alreadyAttachDescription: "Impossible de supprimer un élément déjà utilisé",
+
+ empty: {
+ title: "Aucun article pour le moment!",
+ description: 'Cette section contiendra la liste des éléments.',
+ buttonTitle: 'Ajouter de nouveaux articles',
+ },
+ },
+
+ more: {
+ estimate: "Estimations",
+ items: "Articles",
+ reports: "Rapports",
+ settings: "Paramètres",
+ logout: "Se déconnecter",
+ },
+
+ categories: {
+
+ title: "Nom de catégorie",
+ description: "La description",
+
+ alertDescription: "Vous ne pourrez pas récupérer cette catégorie",
+ alreadyUsed: "Catégorie déjà utilisée",
+
+ empty: {
+ title: "Aucune catégorie pour le moment!",
+ description: "Cette section contiendra la liste des catégories.",
+ buttonTitle: "Ajouter une nouvelle catégorie"
+ }
+ },
+
+ settings: {
+ accountSettings: "réglage du compte",
+ companyInformation: "Compagnie",
+ preference: "Préférences",
+ LanguageAndCurrency: "Langue et devise",
+ notification: "Les notifications",
+ taxes: "Types de taxe",
+ expenseCategory: "Catégories de dépenses",
+
+ endpoint: "point final api",
+
+ company: {
+ name: "Nom de la compagnie",
+ email: "Email",
+ phone: "Téléphone",
+ address: "Adresse",
+ street1: "Rue 1",
+ street2: "Rue # 2",
+ zipcode: "Code postal",
+ website: "Site Internet",
+ logo: "Logo d'entreprise"
+ },
+
+ account: {
+ name: "Nom",
+ email: "Email",
+ password: "Mot de passe",
+ confirmPassword: "Confirmez le mot de passe"
+ },
+
+ notifications: {
+ send: "Envoyer des notifications à ",
+ invoiceViewed: "Facture consultée",
+ invoiceViewedUpdated: "Paramètre de notification mis à jour avec succès",
+ invoiceViewedDescription: "Lorsque votre client visualise le lien de facturation envoyé par courrier électronique.",
+
+ estimateViewed: "Estimation vue",
+ estimateViewedDescription: "Lorsque votre client visualise le lien de devis envoyé par courrier électronique.",
+ estimateViewedUpdated: "Paramètre de notification mis à jour avec succès",
+ },
+
+ preferences: {
+ currency: "Devise",
+ currencyPlaceholder: "Sélectionnez la devise",
+ language: "La langue",
+ languagePlaceholder: "Choisir la langue",
+ timeZone: "Fuseau horaire",
+ timeZonePlaceholder: "Sélectionner le fuseau horaire",
+ dateFormat: "Format de date",
+ dateFormatPlaceholder: "Sélectionnez le format de date",
+ fiscalYear: "Année financière",
+ fiscalYearPlaceholder: "Sélectionnez l'exercice financier",
+ discountSetting: "Réglage de remise",
+ discountPerItem: "Remise par article",
+ discountPerItemPlaceholder: "Activez cette option si vous souhaitez ajouter une remise à des postes de facture individuels. Par défaut, les remises sont ajoutées directement à la facture.",
+ taxPerItem: "Taxe par article",
+ taxPerItemPlaceholder: "Activez cette option si vous souhaitez ajouter une taxe à des postes de facture individuels. Par défaut, les taxes sont ajoutées directement à la facture.",
+
+ settingUpdate: "Réglage mis à jour avec succès"
+ }
+ },
+
+ login: {
+ email: "Entrer votre Email",
+ password: "Tapez votre mot de passe",
+ invalid: "Les informations d'identification invalides"
+ },
+ logout: {
+ confirmation: "Êtes-vous sûr de vouloir vous déconnecter?",
+ title: "Se déconnecter"
+ },
+ forgot: {
+ emailLabel: "Entrez votre email et nous vous enverrons le lien de réinitialisation du mot de passe",
+ emailPlaceholder: "Entrer votre Email",
+ emailSendTitle: "Vérifiez votre email",
+ emailSendDescription: "Un lien de réinitialisation du mot de passe a été envoyé à votre adresse e-mail. Veuillez suivre les instructions sur l'e-mail pour créer un nouveau mot de passe.",
+ emailSendError: "Le courrier électronique n'a pas pu être envoyé à cette adresse électronique.",
+ },
+
+ lostInternet: {
+ title: "Connexion perdue",
+ description: "Veuillez vérifier vos paramètres de téléphonie mobile ou Wifi pour vérifier votre connectivité Internet, puis réessayez."
+ },
+
+ updateApp: {
+ title: "La version de l'application a expiré!",
+ description: "Cette version de l'application est obsolète. Veuillez installer la dernière mise à jour pour continuer à utiliser Crater."
+ },
+
+ endpoint: {
+ endpointURL: 'URL du noeud final',
+ endpointDesc: "L'URL ci-dessus sera utilisé pour vous connecter à votre installation auto-hébergée de crater.",
+ urlPlaceHolder: 'p ex: https://craterapp.com',
+ alertInvalidUrl: "URL invalide",
+ },
+
+ estimates: {
+
+ tabs: {
+ SENT: "Envoyé",
+ DRAFT: "Brouillon",
+ ALL: "Tout"
+ },
+
+ estimateDate: "Date",
+ expiryDate: "Date d'expiration",
+ discount: 'REMISE',
+ estimateNumber: "Numéro d'estimation",
+ template: "Modèle",
+ templatePlaceholder: "Sélectionner ou ajouter un nouveau modèle",
+ customer: "Client",
+ customerPlaceholder: "Sélectionner un client",
+ items: "Articles",
+ unit: "Unité",
+ taxPlaceholder: "+ Ajouter une taxe",
+ addItem: "Ajouter un item",
+ notes: "Remarques",
+ notePlaceholder: "Notes supplémentaires visibles sur devis",
+
+ fromDate: "Partir de la date",
+ toDate: "À ce jour",
+ status: "Statut",
+ statusPlaceholder: "Sélectionnez un statut",
+
+ subtotal: "TOTAL",
+ tax: "IMPÔT",
+ totalAmount: "MONTANT TOTAL",
+
+ alert: {
+ convertToInvoiceDescription: "Êtes-vous sûr de vouloir convertir cette estimation en facture?",
+ removeDescription: "Vous ne pourrez pas récupérer cette estimation",
+ lessAmount: "Le montant total ne devrait pas être inférieur à 0",
+ draftTitle: "Sauvegarder les modifications?",
+ },
+
+ empty: {
+ sent: {
+ title: "Vous n'avez pas envoyé d'estimations",
+ description: "Cette section contiendra la liste des estimations envoyées.",
+ },
+ draft: {
+ title: "Vous n'avez pas d'estimation provisoire",
+ description: "Cette section contiendra la liste des projets d’estimations.",
+ },
+ all: {
+ title: "Vous n'avez créé aucune estimation",
+ description: "Cette section contiendra la liste des estimations.",
+ },
+
+ buttonTitle: "Ajouter une nouvelle estimation"
+ },
+
+ actions: {
+ downloadPdf: 'Télécharger le PDF',
+ sendEstimate: 'Envoyer une estimation',
+ editEstimate: "Modifier l'estimation",
+ delete: 'Effacer',
+ convertToInvoice: 'Convertir en facture',
+ markAsSent: 'Marquer comme envoyé',
+ markAsAccepted: 'Marquer comme accepté',
+ markAsRejected: 'Marquer comme rejeté',
+ }
+ },
+
+ currencies: {
+ title: 'Sélectionnez la devise',
+ empty: {
+ title: "Aucune devise pour le moment!",
+ }
+ },
+
+ languages: {
+ title: 'Choisir la langue',
+ empty: {
+ title: "Pas encore de langues!",
+ description: "Cette section contiendra la liste des langues.",
+ }
+ },
+
+ timeZones: {
+ title: 'Sélectionnez le fuseau horaire',
+ empty: {
+ title: "Pas encore de TimeZones!",
+ description: "Cette section contiendra la liste des fuseaux horaires.",
+ }
+ },
+
+ dateFormats: {
+ title: 'Sélectionnez le format de date',
+ empty: {
+ title: "Aucun format de date pour le moment!",
+ description: "Cette section contiendra la liste des formats de date.",
+ }
+ },
+
+ fiscalYears: {
+ title: "Sélectionnez l'exercice",
+ empty: {
+ title: "Aucun exercice financier encore!",
+ description: "Cette section contient la liste des exercices financiers",
+ }
+ },
+
+ reports: {
+ sales: 'Ventes',
+ profitAndLoss: 'Perte de profit',
+ expenses: 'Les dépenses',
+ taxes: 'Les taxes',
+ dateRange: 'Sélectionner une plage de dates',
+ fromDate: 'Partir de la date',
+ toDate: 'À ce jour',
+ reportType: 'Type de rapport',
+
+ byCustomer: 'Par le client',
+ byItem: 'Par article',
+
+ today: "Today",
+ thisWeek: 'Cette semaine',
+ thisMonth: 'Ce mois-ci',
+ thisQuarter: 'Ce trimestre',
+ thisYear: 'Cette année',
+ currentFiscalQuarter: 'Trimestre financier en cours',
+ currentFiscalYear: 'Année fiscale en cours',
+ previousWeek: 'Semaine précédente',
+ previousMonth: 'Le mois précédent',
+ previousQuarter: 'Trimestre précédent',
+ previousYear: 'Année précédente',
+ previousFiscalQuarter: 'Trimestre précédent',
+ previousFiscalYear: 'Année fiscale précédente',
+ custom: 'Douane',
+
+ },
+
+ taxes: {
+
+ title: 'Les taxes',
+
+ type: "Nom de la taxe",
+ description: "La description",
+ percentage: "Pourcentage d'impôt",
+ compoundTax: "Taxe composée",
+
+ alertDescription: "Vous ne pourrez pas récupérer cette taxe",
+ alreadyUsed: "Taxe déjà utilisée",
+
+ empty: {
+ title: "Aucun type de taxe pour le moment!",
+ description: 'Cette section contiendra la liste des types de taxe.',
+ buttonTitle: 'Ajouter un type de taxe',
+ },
+ },
+
+
+ filePicker: {
+ file: "Cliquez ici pour choisir un fichier",
+ permission: "Désolé, nous avons besoin d'autorisations de rouleau de caméra pour que cela fonctionne!"
+ },
+
+ alert: {
+ title: "Êtes-vous sûr?",
+ action: {
+ discard: "Jeter",
+ saveAsDraft: "Enregistrer comme brouillon"
+ }
+ },
+
+ button: {
+
+ remove: "Retirer",
+ save: "sauver",
+ edit: "Modifier",
+ retry: "Recommencez",
+ done: "Terminé",
+ skip: "Sauter",
+ clear: "Clair",
+
+ // Invoices
+ viewPdf: "Voir PDF",
+
+ // Auth
+ singIn: "se connecter",
+ singInGoogle: "Connectez-vous avec google",
+ forget: "Mot de passe oublié?",
+
+ update: "Mise à jour",
+ updateCapital: "MISE À JOUR",
+ chooseTemplate: "Choisir le modèle",
+
+ // Reports
+ generateReport: "Générer un rapport",
+
+ recoveryEmail: "Envoyer le lien de réinitialisation",
+ recoveryEmailAgain: "Envoyer à nouveau",
+ },
+
+ filter: {
+ empty: {
+ filterTitle: "Aucun résultat trouvé"
+ }
+ },
+
+ search: {
+ title: "Chercher",
+ noResult: 'Aucun résultat pour "{{search}}"',
+ noSearchResult: 'Aucun résultat pour',
+ },
+
+ validation: {
+ Customer: "Client",
+ Invoice: "Facture d'achat",
+ required: "Le champ requis est vide",
+ field: "{{hint}} Champ requis",
+ choose: "Choisissez au moins un article",
+ email: "S'il vous plaît, mettez une adresse email valide",
+ passwordCompare: "Les mots de passe ne correspondent pas",
+ minimumNumber: "doit être supérieur à 0",
+ maximumNumber: "doit être inférieur à {{maxNumber}}",
+ numeric: "doit être numérique",
+ moreThanDue: "{{hint}} Ne devrait pas être plus que le montant dû.",
+ url: "URL invalide (p ex: https://craterapp.com)"
+ },
+
+};
\ No newline at end of file
diff --git a/src/api/lang/i18n.js b/src/api/lang/i18n.js
new file mode 100644
index 00000000..1c958802
--- /dev/null
+++ b/src/api/lang/i18n.js
@@ -0,0 +1,18 @@
+import * as Localization from 'expo-localization';
+import Lng from 'i18n-js';
+
+import { en } from './en';
+import { fr } from './fr';
+import { es } from './es';
+
+Lng.fallbacks = true;
+
+Lng.translations = {
+ en,
+ fr,
+ es
+};
+
+Lng.locale = Localization.locale;
+
+export default Lng;
diff --git a/src/api/request.js b/src/api/request.js
new file mode 100644
index 00000000..6b2defc0
--- /dev/null
+++ b/src/api/request.js
@@ -0,0 +1,170 @@
+// @flow
+
+import { AsyncStorage } from 'react-native';
+// import RNFetchBlob from 'rn-fetch-blob'
+
+import { NavigationActions } from 'react-navigation';
+import { store } from '../store';
+import { env } from '../config';
+import { NAVIGATION_PERSIST_KEY } from './consts/core';
+import { ROUTES } from '../navigation/routes';
+import { isIosPlatform, checkConnection, checkExpiredToken } from './helper';
+import { resetIdToken } from '../features/authentication/actions';
+
+type IProps = {
+ path: string,
+ method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE',
+ headers?: Object,
+ body?: Object,
+};
+
+export default class Request {
+ static async getIdToken() {
+ const data = await AsyncStorage.getItem(NAVIGATION_PERSIST_KEY);
+
+ return data ? JSON.parse(data).auth.idToken : null;
+ }
+
+ static get(params) {
+ return this.request({ method: 'GET', ...params });
+ }
+
+ static post(params) {
+ return this.request({ method: 'POST', ...params });
+ }
+
+ static put(params) {
+ return this.request({ method: 'PUT', ...params });
+ }
+
+ static delete(params) {
+ return this.request({ method: 'DELETE', ...params });
+ }
+
+ static patch(params) {
+ return this.request({ method: 'PATCH', ...params });
+ }
+ static async request({
+ path,
+ method,
+ body,
+ headers = {},
+ image,
+ fullResponse = false,
+ imageName = '',
+ isAuthRequired = true,
+ isPing = null,
+ type = 'create',
+ }: IProps) {
+ const reduxStore = store.getState();
+
+ const { idToken, expiresIn = null } = reduxStore.auth;
+ const { endpointApi, company } = reduxStore.global;
+
+ let apiUrl = (endpointApi !== null && typeof endpointApi !== 'undefined') ? endpointApi : env.ENDPOINT_API
+
+ if (isPing) {
+ apiUrl = isPing
+ }
+
+ const url = `${apiUrl}${path}`;
+ const isConnected = await checkConnection()
+
+ const isExpired = checkExpiredToken(expiresIn)
+
+ if (!isConnected) {
+ store.dispatch(
+ NavigationActions.navigate({
+ routeName: ROUTES.LOST_CONNECTION,
+ }),
+ );
+ return
+ }
+
+ if (isExpired && isAuthRequired) {
+ store.dispatch(resetIdToken());
+ store.dispatch(
+ NavigationActions.navigate({
+ routeName: ROUTES.AUTH,
+ }),
+ );
+ return
+ }
+
+
+ const ID_TOKEN = await this.getIdToken();
+ const defaultHeaders = image
+ ? {
+ Authorization: `Bearer ${idToken}` || `Bearer ${ID_TOKEN}`,
+ company: company ? company.id : 1
+ }
+ : {
+ Authorization: `Bearer ${idToken}` || `Bearer ${ID_TOKEN}`,
+ 'Content-Type': 'application/json',
+ company: company ? company.id : 1
+ };
+
+ const formData = new FormData();
+
+ if (image) {
+ const uri = image.uri;
+ const uriParts = uri.split('.');
+ const fileType = uriParts[uriParts.length - 1];
+
+ formData.append(imageName, JSON.stringify({
+ name: `${imageName}.${fileType}`,
+ data: image.base64.trimRight()
+ }))
+
+ type && formData.append('type', type)
+
+ for (const key in body) {
+ if (body.hasOwnProperty(key)) {
+ formData.append(key, body[key]);
+ }
+ }
+ }
+
+ let options = {
+ method,
+ body: image ? formData : JSON.stringify(body),
+ headers: { ...defaultHeaders, ...headers },
+ };
+
+ return fetch(url, options).then((response) => {
+ const {
+ headers: { map },
+ } = response;
+
+ if (response.status === 500) {
+ throw response;
+ }
+
+ if (response.status === 401) {
+ store.dispatch(
+ NavigationActions.navigate({
+ routeName: ROUTES.AUTH,
+ }),
+ );
+ }
+
+ if (response.status === 403 || response.status === 404) {
+
+ throw response;
+ }
+
+ if (response.ok) {
+ if (response.status === 204) {
+ return Promise.resolve();
+ } else {
+ if (fullResponse) {
+ return response;
+ }
+
+ return response.json().then((v) => v);
+ }
+ }
+ throw response;
+ });
+ }
+}
diff --git a/src/api/validation.js b/src/api/validation.js
new file mode 100644
index 00000000..2ce00906
--- /dev/null
+++ b/src/api/validation.js
@@ -0,0 +1,70 @@
+// @flow
+import React from 'react'
+import {
+ EMAIL_REGEX,
+ URL_REGEX
+} from './consts';
+
+type IValidationOptions = {
+ fieldName?: string,
+};
+type ErrorType =
+ | 'emailFormat'
+ | 'required'
+ | 'requiredField'
+ | 'itemField'
+ | 'requiredCheckArray'
+ | 'minNumberRequired'
+ | 'maxNumberRequired'
+ | 'isNumberFormat'
+ | 'passwordCompared'
+ | 'moreThanDue'
+ | 'urlFormat'
+
+export function getError(
+ value: string,
+ errorTypes: Array,
+ options: IValidationOptions = {},
+) {
+ const { fieldName, minNumber, maxNumber } = options;
+
+ const errorTypeMap = {
+
+ emailFormat: () => (EMAIL_REGEX.test(value) ? null : "validation.email"),
+
+ required: () => (!value ? "validation.required" : null),
+
+ requiredField: () => (!value ? "validation.field" : null),
+
+ itemField: () => (!value ? "validation.choose" : null),
+
+ requiredCheckArray: () => (value && value.length ? null : "validation.choose"),
+
+ minNumberRequired: () => (value <= minNumber ? getMinNumberError(fieldName, minNumber) : null),
+
+ maxNumberRequired: () => {
+ return (value > maxNumber ? ("validation.maximumNumber") : null)
+ },
+
+ isNumberFormat: () => (
+ isNaN(Number(value)) ? "validation.numeric" : null
+ ),
+ passwordCompared: () => (
+ value ? (value === fieldName ? null : "validation.passwordCompare") :
+ fieldName ? (value === fieldName ? null : "validation.passwordCompare") : null
+ ),
+
+ moreThanDue: () => ("validation.moreThanDue"),
+
+ urlFormat: () => (URL_REGEX.test(value) ? null : "validation.url"),
+
+ };
+
+ const errorType = errorTypes.find((error) => errorTypeMap[error] && errorTypeMap[error]());
+
+ return errorType ? errorTypeMap[errorType]() : null;
+}
+
+
+export const getMinNumberError = (fieldName, minNumber) =>
+ `validation.minimumNumber`;
diff --git a/src/assets/crater-logo.png b/src/assets/crater-logo.png
new file mode 100644
index 00000000..860d5f99
Binary files /dev/null and b/src/assets/crater-logo.png differ
diff --git a/src/assets/empty-customers-icon.png b/src/assets/empty-customers-icon.png
new file mode 100644
index 00000000..94e8a3bf
Binary files /dev/null and b/src/assets/empty-customers-icon.png differ
diff --git a/src/assets/empty-estimates-icon.png b/src/assets/empty-estimates-icon.png
new file mode 100644
index 00000000..6dfde04b
Binary files /dev/null and b/src/assets/empty-estimates-icon.png differ
diff --git a/src/assets/empty-expenses-icon.png b/src/assets/empty-expenses-icon.png
new file mode 100644
index 00000000..2d920e46
Binary files /dev/null and b/src/assets/empty-expenses-icon.png differ
diff --git a/src/assets/empty-invoices-icon.png b/src/assets/empty-invoices-icon.png
new file mode 100644
index 00000000..51a7e451
Binary files /dev/null and b/src/assets/empty-invoices-icon.png differ
diff --git a/src/assets/empty-items-icon.png b/src/assets/empty-items-icon.png
new file mode 100644
index 00000000..579ccfd7
Binary files /dev/null and b/src/assets/empty-items-icon.png differ
diff --git a/src/assets/empty-payments-icon.png b/src/assets/empty-payments-icon.png
new file mode 100644
index 00000000..747236fa
Binary files /dev/null and b/src/assets/empty-payments-icon.png differ
diff --git a/src/assets/envelop.png b/src/assets/envelop.png
new file mode 100644
index 00000000..30b3c59e
Binary files /dev/null and b/src/assets/envelop.png differ
diff --git a/src/assets/fonts/Poppins-Bold.ttf b/src/assets/fonts/Poppins-Bold.ttf
new file mode 100755
index 00000000..6e26de76
Binary files /dev/null and b/src/assets/fonts/Poppins-Bold.ttf differ
diff --git a/src/assets/fonts/Poppins-Light.ttf b/src/assets/fonts/Poppins-Light.ttf
new file mode 100755
index 00000000..52d424ba
Binary files /dev/null and b/src/assets/fonts/Poppins-Light.ttf differ
diff --git a/src/assets/fonts/Poppins-Medium.ttf b/src/assets/fonts/Poppins-Medium.ttf
new file mode 100755
index 00000000..89aae6b6
Binary files /dev/null and b/src/assets/fonts/Poppins-Medium.ttf differ
diff --git a/src/assets/fonts/Poppins-Regular.ttf b/src/assets/fonts/Poppins-Regular.ttf
new file mode 100755
index 00000000..441d3baa
Binary files /dev/null and b/src/assets/fonts/Poppins-Regular.ttf differ
diff --git a/src/assets/fonts/Poppins-SemiBold.ttf b/src/assets/fonts/Poppins-SemiBold.ttf
new file mode 100755
index 00000000..3b8622f6
Binary files /dev/null and b/src/assets/fonts/Poppins-SemiBold.ttf differ
diff --git a/src/assets/google.png b/src/assets/google.png
new file mode 100644
index 00000000..36cc85c0
Binary files /dev/null and b/src/assets/google.png differ
diff --git a/src/assets/logo-light.png b/src/assets/logo-light.png
new file mode 100644
index 00000000..fa63b344
Binary files /dev/null and b/src/assets/logo-light.png differ
diff --git a/src/assets/lost-connection.png b/src/assets/lost-connection.png
new file mode 100644
index 00000000..6fdeef0c
Binary files /dev/null and b/src/assets/lost-connection.png differ
diff --git a/src/assets/svg.js b/src/assets/svg.js
new file mode 100644
index 00000000..9df9e064
--- /dev/null
+++ b/src/assets/svg.js
@@ -0,0 +1,22 @@
+
+
+export const CUSTOMERS = `
+
+ `
+
+export const INVOICES = `
+
+ `
+
+export const PAYMETNS = `
+
+ `
+
+export const EXPENSES = `
+
+ `
+
+
+export const MORE = `
+
+ `
diff --git a/src/components/AppLoader/index.js b/src/components/AppLoader/index.js
new file mode 100644
index 00000000..cda0c745
--- /dev/null
+++ b/src/components/AppLoader/index.js
@@ -0,0 +1,32 @@
+// @flow
+
+import React from 'react';
+import { connect } from 'react-redux';
+import { ActivityIndicator, View } from 'react-native';
+
+import styles from './styles';
+
+type IProps = {
+ appLoading: boolean,
+};
+
+const AppLoaderComponent = (props: IProps) => {
+ const { appLoading } = props;
+
+ return (
+
+ {appLoading && }
+
+ );
+};
+
+const mapStateToProps = ({ global }) => ({
+ appLoading: global.loading,
+});
+
+const mapDispatchToProps = {};
+
+export const AppLoader = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(AppLoaderComponent);
diff --git a/src/components/AppLoader/styles.js b/src/components/AppLoader/styles.js
new file mode 100644
index 00000000..c6638ce6
--- /dev/null
+++ b/src/components/AppLoader/styles.js
@@ -0,0 +1,19 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../styles/colors';
+
+
+export default StyleSheet.create({
+ loadingWrapper: {
+ backgroundColor: colors.dark2,
+ opacity: 0.8,
+ display: 'flex',
+ justifyContent: 'center',
+ alignContent: 'center',
+ position: 'absolute',
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0,
+ zIndex: 1,
+ },
+});
diff --git a/src/components/AssetImage/AssetImage.js b/src/components/AssetImage/AssetImage.js
new file mode 100644
index 00000000..00a8195e
--- /dev/null
+++ b/src/components/AssetImage/AssetImage.js
@@ -0,0 +1,45 @@
+import React, { Component } from 'react';
+import { Image, View, ActivityIndicator } from 'react-native';
+
+type IProps = {
+ imageStyle: Object,
+ imageName: String,
+ uri: Boolean,
+ imageProps: Object,
+ loadingImageStyle: Object
+};
+export class AssetImage extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ loading: false,
+ };
+ }
+
+ imageLoad() {
+ const { loading } = this.state;
+ this.setState({ loading: !loading });
+ }
+
+ render() {
+ const { imageSource, imageStyle, loadingImageStyle, uri, imageProps } = this.props;
+ const { loading } = this.state;
+ return (
+
+ {loading &&
+ }
+ this.imageLoad()}
+ onLoad={() => this.imageLoad()}
+ {...imageProps}
+ />
+
+ );
+ }
+}
+
+export default AssetImage;
diff --git a/src/components/AssetImage/index.js b/src/components/AssetImage/index.js
new file mode 100644
index 00000000..2df2f320
--- /dev/null
+++ b/src/components/AssetImage/index.js
@@ -0,0 +1 @@
+export * from './AssetImage';
diff --git a/src/components/AssetImage/styles.js b/src/components/AssetImage/styles.js
new file mode 100644
index 00000000..ab00ec9a
--- /dev/null
+++ b/src/components/AssetImage/styles.js
@@ -0,0 +1,3 @@
+import { StyleSheet } from 'react-native';
+
+export const styles = StyleSheet.create({});
diff --git a/src/components/Button/Button.js b/src/components/Button/Button.js
new file mode 100644
index 00000000..c9251923
--- /dev/null
+++ b/src/components/Button/Button.js
@@ -0,0 +1,264 @@
+
+// @flow
+
+import React, { Component } from 'react';
+import { View } from 'react-native';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import styles from './styles';
+import { colors } from '../../styles/colors';
+import { Button } from 'react-native-elements';
+import { LinearGradient } from 'expo-linear-gradient';
+import { AssetImage } from '../AssetImage';
+import { BUTTON_TYPE, BUTTON_COLOR } from '../../api/consts/core';
+
+type IProps = {
+ children: any,
+ onPress: Function,
+ loading: boolean,
+ disabled: boolean,
+ whiteButton: boolean,
+ style: Object,
+ iconPlacement: any,
+ btnTitle: String,
+ type: String,
+ iconName: String,
+ imageIcon: Boolean,
+ isGradient: Boolean,
+ raised: Boolean,
+ imageSource: String | any,
+ buttonType: String,
+ containerStyle: Object,
+};
+export class CtGradientButton extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ buttonFocus: false
+ };
+ }
+
+ componentWillUpdate(nextProps, nextState) {
+
+ const { loading } = nextProps
+ const { buttonFocus } = nextState
+
+ if (!loading && buttonFocus) {
+ this.setState({
+ buttonFocus: false
+ })
+ }
+ }
+
+ onBtnPress = () => {
+
+ this.setState({
+ buttonFocus: true
+ })
+
+ const { onPress } = this.props
+ onPress && onPress()
+ }
+
+ render() {
+
+ const {
+ style,
+ iconPlacement,
+ type = "solid",
+ loading = false,
+ btnTitle,
+ iconName,
+ imageIcon = false,
+ imageSource,
+ raised,
+ buttonColor = BUTTON_COLOR.PRIMARY,
+ containerStyle,
+ buttonContainerStyle,
+ hasFocus = true,
+ } = this.props
+
+ const { buttonFocus } = this.state
+
+ return (
+
+
+ ) : (
+
+ )
+ }
+ {...iconPlacement}
+ containerStyle={[
+ styles.containerStyle,
+ raised && styles.containerShadow,
+ containerStyle && containerStyle,
+ hasFocus && buttonFocus && { borderColor: colors[`${buttonColor}Light`] }
+ ]}
+ buttonStyle={[
+ styles.buttonStyle,
+ style,
+ type === BUTTON_TYPE.OUTLINE ?
+ [{ borderColor: colors[buttonColor] }, styles.buttonOutline] :
+ {
+ backgroundColor: colors[buttonColor],
+ borderColor: colors[buttonColor]
+ },
+ ]}
+ onPress={() => this.onBtnPress()}
+ type={type}
+ title={btnTitle}
+ titleStyle={[
+ styles.titleStyle,
+ type === BUTTON_TYPE.OUTLINE && { color: colors[buttonColor] }
+ ]}
+ loading={loading && buttonFocus}
+ loadingStyle={{ opacity: 0.7 }}
+ loadingProps={{ color: colors.darkGray }}
+ linearGradientProps={{
+ colors: [colors.primary, colors.primaryLight],
+ start: { x: 0, y: 0.5 },
+ end: { x: 1, y: 0.5 },
+ }}
+ disabled={loading}
+ disabledStyle={[
+ styles.buttonStyle,
+ style,
+ type === BUTTON_TYPE.OUTLINE ?
+ [{ borderColor: colors[buttonColor] }, styles.buttonOutline] :
+ {
+ backgroundColor: colors[buttonColor],
+ borderColor: colors[buttonColor],
+ opacity: 0.7,
+ },
+ ]}
+ disabledTitleStyle={
+ styles.titleStyle,
+ {
+ color: (type === BUTTON_TYPE.OUTLINE) ?
+ colors[buttonColor] : colors.white,
+ }
+ }
+ ViewComponent={LinearGradient}
+ />
+
+ );
+ }
+}
+
+
+
+
+export class CtButton extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ buttonFocus: false
+ };
+ }
+
+ componentWillUpdate(nextProps, nextState) {
+
+ const { loading } = nextProps
+ const { buttonFocus } = nextState
+
+ if (!loading && buttonFocus) {
+ this.setState({
+ buttonFocus: false
+ })
+ }
+ }
+
+ onBtnPress = () => {
+ const { onPress } = this.props
+
+ this.setState({
+ buttonFocus: true
+ })
+
+ onPress && onPress()
+ }
+
+ render() {
+
+ const {
+ style,
+ iconPlacement,
+ type = "solid",
+ loading = false,
+ btnTitle,
+ iconName,
+ imageIcon = false,
+ imageSource,
+ raised,
+ buttonColor = BUTTON_COLOR.PRIMARY,
+ containerStyle,
+ buttonContainerStyle,
+ hasFocus = true,
+ } = this.props
+
+ const { buttonFocus } = this.state
+
+ return (
+
+
+ ) : (
+
+ )
+ }
+ {...iconPlacement}
+ containerStyle={[
+ styles.containerStyle,
+ raised && styles.containerShadow,
+ containerStyle && containerStyle,
+ hasFocus && buttonFocus && { borderColor: colors[`${buttonColor}Light`] }
+ ]}
+ buttonStyle={[
+ styles.buttonStyle,
+ style,
+ type === BUTTON_TYPE.OUTLINE ?
+ [{ borderColor: colors[buttonColor] }, styles.buttonOutline] :
+ {
+ backgroundColor: colors[buttonColor],
+ borderColor: colors[buttonColor]
+ },
+ ]}
+ onPress={() => this.onBtnPress()}
+ type={type}
+ title={btnTitle}
+ loading={loading && buttonFocus}
+ loadingStyle={{ opacity: 0.7 }}
+ loadingProps={{ color: colors.darkGray }}
+ titleStyle={[
+ styles.titleStyle,
+ type === BUTTON_TYPE.OUTLINE && { color: colors[buttonColor] }
+ ]}
+ disabled={loading}
+ disabledStyle={[
+ styles.buttonStyle,
+ style,
+ type === BUTTON_TYPE.OUTLINE ?
+ [{ borderColor: colors[buttonColor] }, styles.buttonOutline] :
+ {
+ backgroundColor: colors[buttonColor],
+ borderColor: colors[buttonColor],
+ opacity: 0.7,
+ },
+ ]}
+ disabledTitleStyle={
+ styles.titleStyle,
+ {
+ color: (type === BUTTON_TYPE.OUTLINE) ?
+ colors[buttonColor] : colors.white,
+ }
+ }
+ />
+
+ );
+ }
+}
+
diff --git a/src/components/Button/index.js b/src/components/Button/index.js
new file mode 100644
index 00000000..4c479d29
--- /dev/null
+++ b/src/components/Button/index.js
@@ -0,0 +1,2 @@
+export { CtButton, CtGradientButton } from "./Button";
+
diff --git a/src/components/Button/styles.js b/src/components/Button/styles.js
new file mode 100644
index 00000000..19525b95
--- /dev/null
+++ b/src/components/Button/styles.js
@@ -0,0 +1,53 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+import { isIosPlatform } from '../../api/helper';
+
+export default StyleSheet.create({
+ buttonStyle: {
+ backgroundColor: 'transparent',
+ borderWidth: 1,
+ borderRadius: 5,
+ margin: 0,
+ paddingVertical: 7,
+ },
+ buttonContainer: {
+
+ },
+ containerStyle: {
+ borderWidth: 5,
+ borderRadius: 10,
+ borderColor: 'transparent',
+ padding: 0,
+ backgroundColor: 'transparent',
+ },
+ containerShadow: {
+ borderWidth: 1,
+ borderRadius: 2,
+ borderColor: colors.lightGray,
+ shadowColor: colors.lightGray,
+ backgroundColor: colors.white,
+ borderTopWidth: 0.3,
+ shadowOffset: { width: 0, height: 7 },
+ shadowOpacity: 0.8,
+ shadowRadius: 5,
+ elevation: 7,
+ marginLeft: 5,
+ marginRight: 5,
+ marginTop: 10,
+ },
+ titleStyle: {
+ fontSize: 14,
+ fontFamily: fonts.poppinsMedium,
+ color: colors.white,
+ },
+ imageIcon: {
+ width: 20,
+ height: 20,
+ marginRight: 15,
+ },
+
+ buttonOutline: {
+ backgroundColor: 'transparent'
+ },
+});
diff --git a/src/components/Content/index.js b/src/components/Content/index.js
new file mode 100644
index 00000000..67a80a8c
--- /dev/null
+++ b/src/components/Content/index.js
@@ -0,0 +1,36 @@
+// @flow
+
+import React, { Fragment } from 'react';
+import { View } from 'react-native';
+import { Loading, Empty } from '..';
+
+type IProps = {
+ loading: boolean,
+ error: string,
+ children: any,
+ empty: string,
+ onRetry: Function,
+};
+
+export const Content = ({
+ children,
+ withLoading = false,
+ loading,
+ loadingProps,
+ emptyProps,
+}: IProps) => {
+ if (emptyProps && emptyProps.is) {
+ return ;
+ }
+
+ if (withLoading) {
+ return (
+
+ {children}
+ {loadingProps && loadingProps.is && }
+
+ );
+ }
+
+ return loadingProps && loadingProps.is ? : children;
+};
diff --git a/src/components/CurrencyFormat/index.js b/src/components/CurrencyFormat/index.js
new file mode 100644
index 00000000..0e7f149b
--- /dev/null
+++ b/src/components/CurrencyFormat/index.js
@@ -0,0 +1,48 @@
+import React, { Component } from 'react';
+import { View, Text } from 'react-native';
+import { formatMoney } from '../../api/helper';
+import { styles, SymbolStyle } from './styles';
+
+type IProps = {
+ style: Object,
+ amount: String,
+ currency: Object,
+ preText: String,
+ containerStyle: Object,
+ currencyStyle: Object
+};
+export class CurrencyFormat extends Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ const {
+ style,
+ amount,
+ currency,
+ preText,
+ containerStyle,
+ currencyStyle,
+ } = this.props;
+ const { symbol, money } = formatMoney(amount, currency)
+ return (
+
+
+ {preText && preText}
+
+
+ {symbol}
+ {' '}
+
+ {money}
+
+ );
+ }
+}
diff --git a/src/components/CurrencyFormat/styles.js b/src/components/CurrencyFormat/styles.js
new file mode 100644
index 00000000..83c4bf8c
--- /dev/null
+++ b/src/components/CurrencyFormat/styles.js
@@ -0,0 +1,15 @@
+import { StyleSheet } from 'react-native';
+import { isIosPlatform } from '../../api/helper';
+
+export const SymbolStyle = {
+ fontFamily: isIosPlatform() ? 'Arial' : 'sans-serif'
+}
+
+export const styles = StyleSheet.create({
+ container: {
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ flexDirection: 'row',
+ },
+});
diff --git a/src/components/DatePickerField/index.js b/src/components/DatePickerField/index.js
new file mode 100644
index 00000000..db297b27
--- /dev/null
+++ b/src/components/DatePickerField/index.js
@@ -0,0 +1,155 @@
+// @flow
+
+import React, { Component } from 'react';
+import { View } from 'react-native';
+import styles from './styles';
+import DateTimePicker from 'react-native-modal-datetime-picker';
+import moment from 'moment';
+import { FakeInput } from '../FakeInput';
+import { connect } from 'react-redux';
+import { DATE_FORMAT } from '../../api/consts';
+
+type IProps = {
+ label: String,
+ icon: String,
+ onChangeCallback: Function,
+ containerStyle: Object,
+ rightIcon: String,
+ displayValue: Boolean,
+ isRequired: Boolean,
+};
+
+export class DatePickerComponent extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ isDateTimePickerVisible: false,
+ value: '',
+ };
+ }
+
+ componentDidMount() {
+ const {
+ input: { value, onChange },
+ dateFormat,
+ selectedDate,
+ selectedDateValue,
+ filter,
+ } = this.props
+
+ if (selectedDate && ((!value) === false)) {
+ this.setState({ value: selectedDate });
+ onChange(selectedDateValue);
+ } else {
+ if (value) {
+
+ let displayDate = moment(value).format(dateFormat);
+ let formDate = moment(value).format(DATE_FORMAT);
+
+ this.setState({
+ value: displayDate,
+ });
+
+ onChange(formDate);
+ }
+ }
+ }
+
+ showHideDateTimePicker = () => {
+ this.setState((prevState) => {
+ return { isDateTimePickerVisible: !prevState.isDateTimePickerVisible };
+ });
+ };
+
+ handleDatePicked = (date) => {
+ const {
+ onChangeCallback,
+ dateFormat,
+ input: { onChange },
+ filter
+ } = this.props;
+
+ let displayDate = moment(date).format(dateFormat);
+
+ let formDate = moment(date).format(DATE_FORMAT);
+
+ this.setState({
+ value: displayDate,
+ });
+
+ this.showHideDateTimePicker();
+
+ onChange(formDate);
+
+ if (filter) {
+ onChangeCallback && onChangeCallback(formDate, displayDate);
+ }
+ else
+ onChangeCallback && onChangeCallback(formDate);
+ };
+
+ getDate = (displayValue) => {
+ const { dateFormat } = this.props
+ if (displayValue) {
+ return displayValue.format(dateFormat)
+ }
+ return null
+ }
+
+ render() {
+ const {
+ label,
+ containerStyle,
+ meta,
+ displayValue,
+ isRequired = false,
+ } = this.props;
+
+ const { isDateTimePickerVisible, value } = this.state;
+ const { input } = this.props
+ const dateValue = displayValue || value
+
+ let pickerOption = {}
+
+ if (dateValue) {
+ pickerOption = { date: new Date(dateValue) }
+ }
+
+ return (
+
+
+ this.showHideDateTimePicker()}
+ meta={meta}
+ isRequired={isRequired}
+ containerStyle={containerStyle}
+ />
+
+
+
+ );
+ }
+}
+
+const mapStateToProps = ({ global }) => ({
+ dateFormat: global.dateFormat,
+});
+
+const mapDispatchToProps = {};
+
+export const DatePickerField = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(DatePickerComponent);
diff --git a/src/components/DatePickerField/styles.js b/src/components/DatePickerField/styles.js
new file mode 100644
index 00000000..c1273695
--- /dev/null
+++ b/src/components/DatePickerField/styles.js
@@ -0,0 +1,7 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+
+export default StyleSheet.create({
+ container: {},
+});
diff --git a/src/components/Divider/index.js b/src/components/Divider/index.js
new file mode 100644
index 00000000..23f708c1
--- /dev/null
+++ b/src/components/Divider/index.js
@@ -0,0 +1,26 @@
+// @flow
+
+import React from 'react';
+import { View, Text } from 'react-native';
+import { Divider } from 'react-native-elements';
+import { styles } from './styles';
+
+type IProps = {
+ title: String,
+ dividerStyle: Object,
+ titleStyle: Object,
+};
+
+export const CtDivider = ({ title, dividerStyle, titleStyle }: IProps) => {
+ return title ? (
+
+
+
+
+ {title}
+
+
+ ) : (
+
+ );
+};
diff --git a/src/components/Divider/styles.js b/src/components/Divider/styles.js
new file mode 100644
index 00000000..6de30eda
--- /dev/null
+++ b/src/components/Divider/styles.js
@@ -0,0 +1,38 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+
+export const styles = StyleSheet.create({
+ dividerContainer: {
+ marginVertical: 20,
+ marginHorizontal: 12,
+ position: 'relative',
+ },
+ divider: {
+ backgroundColor: colors.darkGray,
+ marginVertical: 15,
+ height: 1,
+ },
+ titleContainer: {
+ position: 'absolute',
+ top: 0,
+ borderRadius: 10,
+ height: 30,
+ width: 35,
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: colors.white,
+ left: '45%',
+ },
+ title: {
+ color: colors.darkGray,
+ fontFamily: fonts.poppins,
+ },
+ withoutTitle: {
+ backgroundColor: colors.gray,
+ borderColor: colors.gray,
+ borderWidth: 0.7,
+ marginBottom: 2,
+ },
+});
diff --git a/src/components/Dropdown/index.js b/src/components/Dropdown/index.js
new file mode 100644
index 00000000..06e356f7
--- /dev/null
+++ b/src/components/Dropdown/index.js
@@ -0,0 +1,86 @@
+import React, { Component } from 'react';
+import { View } from 'react-native';
+import ActionSheet from 'react-native-actionsheet'
+import { styles } from './styles';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { TouchableOpacity } from 'react-native-gesture-handler';
+import { colors } from '../../styles/colors';
+
+type IProps = {
+ options: Array,
+ onPress: Function,
+ cancelButtonIndex: Number,
+ destructiveButtonIndex: Number,
+};
+
+export default class Dropdown extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ labelOptions: []
+ };
+ }
+
+ componentWillMount() {
+ const { options } = this.props;
+ const labelOptions = [...options, { label: 'Cancel', value: null }].map(
+ ({ label }) => label,
+ );
+
+ this.setState({ labelOptions })
+ }
+
+ showActionSheet = () => {
+ this.ActionSheet.show()
+ }
+
+ onSelect = (index) => {
+ const { options, onSelect } = this.props;
+
+ const valueOptions = [...options, { label: 'Cancel', value: null }].map(
+ ({ value }) => value,
+ );
+
+ onSelect && onSelect(valueOptions[index]);
+ }
+
+
+
+ render() {
+ const { options, onPress, cancelButtonIndex, destructiveButtonIndex } = this.props;
+ const { labelOptions } = this.state;
+
+ return (
+
+
+
+
+ {labelOptions && (
+ this.ActionSheet = o}
+ tintColor={colors.primary}
+ options={labelOptions}
+ cancelButtonIndex={cancelButtonIndex || 2}
+ destructiveButtonIndex={destructiveButtonIndex || 1}
+ onPress={this.onSelect}
+ />
+ )}
+
+ );
+ }
+}
+
+
diff --git a/src/components/Dropdown/styles.js b/src/components/Dropdown/styles.js
new file mode 100644
index 00000000..906da372
--- /dev/null
+++ b/src/components/Dropdown/styles.js
@@ -0,0 +1,12 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../styles/colors';
+
+export const styles = StyleSheet.create({
+ button: {
+ flexDirection: 'row',
+ marginRight: 10,
+ },
+ iconStyle: {
+ color: colors.primary
+ }
+});
diff --git a/src/components/Empty/index.js b/src/components/Empty/index.js
new file mode 100644
index 00000000..c58a2c96
--- /dev/null
+++ b/src/components/Empty/index.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import { View, Text } from 'react-native';
+import { AssetImage } from '../AssetImage';
+import { styles } from './styles';
+import { CtButton } from '../Button';
+import { BUTTON_TYPE } from '../../api/consts';
+import { colors } from '../../styles/colors';
+
+type IProps = {
+ title: String,
+ description: String,
+ image: String,
+ buttonTitle: String,
+ buttonPress: Function,
+};
+export const Empty = ({
+ title,
+ description,
+ image,
+ buttonTitle,
+ buttonPress,
+}: IProps) => {
+ return (
+
+ {image && (
+
+ )}
+
+
+ {title && title}
+
+
+
+ {description && description}
+
+
+ {buttonTitle && (
+
+ )}
+
+
+
+ );
+};
+
+export default Empty;
diff --git a/src/components/Empty/styles.js b/src/components/Empty/styles.js
new file mode 100644
index 00000000..d5f5810b
--- /dev/null
+++ b/src/components/Empty/styles.js
@@ -0,0 +1,40 @@
+import { StyleSheet } from 'react-native';
+import { fonts } from '../../styles/fonts';
+import { colors } from '../../styles/colors';
+import {
+ widthPercentageToDP as wp,
+ heightPercentageToDP as hp,
+} from 'react-native-responsive-screen';
+
+export const styles = StyleSheet.create({
+ emptyContainer: {
+ flex: 1,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ emptyImage: {
+ height: hp('20%'),
+ width: wp('40%'),
+ resizeMode: "contain",
+ },
+ emptyTitle: {
+ textAlign: "center",
+ color: colors.dark,
+ fontSize: 18,
+ marginTop: 15,
+ fontFamily: fonts.poppinsSemiBold,
+ paddingHorizontal: 10,
+ fontWeight: "600"
+ },
+ emptyDescription: {
+ textAlign: "center",
+ paddingHorizontal: 10,
+ color: colors.veryDarkGray,
+ marginTop: 5
+ },
+ emptyButton: {
+ marginTop: 22,
+ marginHorizontal: 40
+ },
+});
diff --git a/src/components/FakeInput/index.js b/src/components/FakeInput/index.js
new file mode 100644
index 00000000..22913380
--- /dev/null
+++ b/src/components/FakeInput/index.js
@@ -0,0 +1,188 @@
+// @flow
+
+import React, { Component } from 'react';
+import { View, TouchableWithoutFeedback, Text, TouchableOpacity } from 'react-native';
+import { connect } from 'react-redux';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { Input } from 'react-native-elements';
+import styles from './styles';
+import { colors } from '../../styles/colors';
+import { Content } from '../Content';
+import Lng from '../../api/lang/i18n';
+
+type IProps = {
+ label: String,
+ icon: String,
+ onChangeCallback: Function,
+ placeholder: String,
+ containerStyle: Object,
+ rightIcon: String,
+ leftIcon: String,
+ color: String,
+ value: String,
+ fakeInput: any,
+ fakeInputContainerStyle: Object,
+ valueStyle: Object,
+ loading: Boolean,
+ language: String,
+ isRequired: Boolean,
+ leftIconSolid: Boolean,
+ disabled: Boolean,
+};
+
+export class FakeInputComponent extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {};
+ }
+
+ render() {
+ const {
+ label,
+ icon,
+ onChangeCallback,
+ placeholder,
+ containerStyle,
+ fakeInputContainerStyle,
+ rightIcon,
+ leftIcon,
+ fakeInput,
+ color,
+ values,
+ meta: { error, submitFailed } = '',
+ valueStyle,
+ loading = false,
+ placeholderStyle,
+ leftIconStyle,
+ language,
+ isRequired = false,
+ leftIconSolid = true,
+ disabled = false,
+ } = this.props;
+
+ return (
+
+ {label &&
+ {label}
+ {isRequired && (
+ *
+ )}
+ }
+ {fakeInput ? (
+ onChangeCallback && onChangeCallback()}>
+
+ {leftIcon && (
+
+ )}
+ {fakeInput}
+
+
+ ) : (
+
+
+ onChangeCallback && onChangeCallback()
+ }
+ activeOpacity={disabled ? 1 : 0.7}
+ >
+
+
+ {icon && (
+
+ )}
+
+ {values ? (
+
+ {values}
+
+ ) : (
+
+ {placeholder && placeholder}
+
+ )}
+
+ {rightIcon && (
+
+ )}
+
+
+
+ )}
+
+ {submitFailed && error && (
+
+
+ {Lng.t(error, {
+ locale: language,
+ hint: label
+ })}
+
+
+ )}
+
+ );
+ }
+}
+
+
+const mapStateToProps = ({ global }) => ({
+ language: global.language,
+});
+
+const mapDispatchToProps = {};
+
+export const FakeInput = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(FakeInputComponent);
+
diff --git a/src/components/FakeInput/styles.js b/src/components/FakeInput/styles.js
new file mode 100644
index 00000000..559d4905
--- /dev/null
+++ b/src/components/FakeInput/styles.js
@@ -0,0 +1,106 @@
+import { StyleSheet, Platform } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+
+export default StyleSheet.create({
+ container: {
+ marginTop: 10,
+ position: 'relative'
+ },
+ label: {
+ paddingBottom: 7,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+ required: {
+ color: colors.danger
+ },
+ fakeInput: {
+ flexDirection: 'row',
+ justifyContent: "space-between",
+ paddingTop: 10,
+ paddingBottom: 8,
+ ...Platform.select({
+ ios: {
+ paddingTop: 11,
+ paddingBottom: 9,
+ },
+ }),
+ paddingRight: 5,
+ borderWidth: 1,
+ borderColor: colors.lightGray,
+ backgroundColor: colors.white,
+ marginBottom: 10,
+ },
+ loadingFakeInput: {
+ paddingVertical: 11,
+ borderWidth: 1,
+ borderColor: colors.lightGray,
+ backgroundColor: colors.white,
+ marginBottom: 10,
+ },
+ textValue: {
+ paddingLeft: 10,
+ color: colors.secondary,
+ fontSize: 15,
+ fontFamily: fonts.poppins,
+ },
+ placeholderText: {
+ paddingLeft: 10,
+ color: colors.darkGray,
+ fontSize: 15,
+ fontFamily: fonts.poppins,
+ },
+ hasRightIcon: {
+ paddingRight: 15,
+ },
+ rightIcon: {
+ position: 'absolute',
+ top: 14,
+ right: 11,
+ ...Platform.select({
+ ios: {
+ top: 13,
+ right: 11,
+ },
+ }),
+ },
+ leftIcon: {
+ position: 'absolute',
+ zIndex: 20,
+ left: 15,
+ top: 15,
+ fontSize: 16,
+ ...Platform.select({
+ ios: {
+ left: 15,
+ top: 13,
+ },
+ }),
+ },
+ validation: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ bottom: 0,
+ marginBottom: -6,
+ paddingVertical: 2,
+ paddingHorizontal: 5,
+ borderRadius: 2,
+ overflow: 'hidden',
+ flex: 1,
+ zIndex: 100,
+ backgroundColor: colors.danger,
+ },
+ inputError: {
+ borderColor: colors.dangerLight
+ },
+ disabledSelectedValue: {
+ backgroundColor: colors.lightGray
+ },
+ pickerError: {
+ borderColor: colors.dangerLight,
+ borderWidth: 1,
+ }
+});
diff --git a/src/components/FilePicker/index.js b/src/components/FilePicker/index.js
new file mode 100644
index 00000000..67dd78a0
--- /dev/null
+++ b/src/components/FilePicker/index.js
@@ -0,0 +1,189 @@
+import React, { Component } from 'react';
+import {
+ View,
+ Text,
+ TouchableWithoutFeedback,
+ Alert,
+ Linking,
+} from 'react-native';
+import * as IntentLauncher from 'expo-intent-launcher';
+import * as ImagePicker from 'expo-image-picker';
+import * as Permissions from 'expo-permissions';
+import * as FileSystem from 'expo-file-system';
+import { connect } from 'react-redux';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { styles } from './styles';
+import { AssetImage } from '../AssetImage';
+import { colors } from '../../styles/colors';
+import { isIosPlatform } from '../../api/helper';
+import Lng from '../../api/lang/i18n';
+import { Content } from '../Content';
+
+type IProps = {
+ label: String,
+ icon: String,
+ placeholder: String,
+ containerStyle: Object,
+ onChangeCallback: Function,
+ mediaType: String,
+ onGetBase64: Function,
+};
+
+export class FilePickerComponent extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ image: null,
+ loading: false
+ };
+ }
+
+ componentDidMount() {
+ this.getPermissionAsync();
+ }
+
+ getPermissionAsync = async () => {
+ const { language } = this.props
+
+ const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
+ if (status !== 'granted') {
+ Alert.alert(
+ "",
+ Lng.t("filePicker.permission", { locale: language }),
+ [
+ {
+ text: 'Allow',
+ onPress: () => {
+ if (isIosPlatform()) {
+ Linking.openURL('app-settings:');
+ } else {
+ IntentLauncher.startActivityAsync(IntentLauncher.ACTION_MANAGE_APPLICATIONS_SETTINGS);
+ }
+ }
+ },
+ {
+ text: 'Cancel',
+ onPress: () => console.log('cancel'),
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ }
+ }
+
+ onToggleLoading = () => {
+ const { fileLoading } = this.props
+ const { loading } = this.state
+
+ this.setState((prevState) => {
+ return { loading: !prevState.loading }
+ });
+
+ fileLoading && fileLoading(!loading)
+ }
+
+ chooseFile = async () => {
+
+ setTimeout(() => {
+ this.onToggleLoading()
+ }, 1000);
+
+ const { mediaType = 'Images' } = this.props
+
+ let result = await ImagePicker.launchImageLibraryAsync({
+ mediaTypes: ImagePicker.MediaTypeOptions[mediaType],
+ // mediaTypes: ImagePicker.MediaTypeOptions.All,
+ allowsEditing: mediaType === 'Images' ? true : false,
+ base64: true,
+ quality: 1,
+ });
+
+ if (!result.cancelled) {
+ const { onChangeCallback, input: { onChange } } = this.props
+ this.setState({ image: result.uri });
+
+ FileSystem.readAsStringAsync(result.uri, {
+ encoding: FileSystem.EncodingType.Base64
+ }).then((base64) => {
+ const res = { ...result, base64 }
+ onChangeCallback(res)
+ this.onToggleLoading()
+ })
+ .catch(error => {
+ console.error(error);
+ });
+ }
+ else {
+ this.onToggleLoading()
+ }
+ };
+
+
+ render() {
+
+ let { image, loading } = this.state;
+ const {
+ label,
+ containerStyle,
+ imageUrl,
+ language
+ } = this.props;
+
+
+ return (
+
+ {label && {label} }
+
+
+ this.chooseFile()}>
+
+
+ {image !== null || imageUrl ? (
+
+
+
+ ) : (
+
+
+
+
+ {Lng.t("filePicker.file", { locale: language })}
+
+
+
+ )}
+
+
+
+
+ );
+ }
+}
+
+
+const mapStateToProps = ({ global }) => ({
+ language: global.language,
+});
+
+const mapDispatchToProps = {};
+
+export const FilePicker = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(FilePickerComponent);
+
diff --git a/src/components/FilePicker/styles.js b/src/components/FilePicker/styles.js
new file mode 100644
index 00000000..30e4784f
--- /dev/null
+++ b/src/components/FilePicker/styles.js
@@ -0,0 +1,59 @@
+import { StyleSheet } from 'react-native';
+import {
+ widthPercentageToDP as wp,
+ heightPercentageToDP as hp,
+} from 'react-native-responsive-screen';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+import { isIosPlatform } from '../../api/helper';
+
+const container = {
+ borderWidth: 1,
+ borderColor: colors.gray,
+ borderStyle: 'dashed',
+ borderRadius: 4,
+}
+
+export const styles = StyleSheet.create({
+ mainContainer: {
+ position: 'relative',
+ marginTop: 10,
+ },
+ images: {
+ height: 110,
+ resizeMode: "contain",
+ },
+ container: {
+ ...container,
+ alignItems: "center",
+ justifyContent: "center",
+ paddingVertical: 22,
+
+ },
+ loadingContainer: {
+ ...container,
+ height: 100,
+ },
+ uploadContainer: {
+ alignItems: "center",
+ justifyContent: "center",
+ },
+ imageContainer: {
+ ...container,
+ padding: 10
+ },
+ title: {
+ paddingTop: 5,
+ color: colors.darkGray,
+ fontFamily: fonts.poppinsLight,
+ },
+ label: {
+ paddingBottom: 7,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+ loadImage: {
+ width: wp('88%'),
+ }
+});
diff --git a/src/components/Filter/index.js b/src/components/Filter/index.js
new file mode 100644
index 00000000..7a09b904
--- /dev/null
+++ b/src/components/Filter/index.js
@@ -0,0 +1,276 @@
+import React, { Component } from 'react';
+import { View, Modal, TouchableOpacity, Text } from 'react-native';
+import { Field, reset, change } from 'redux-form';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import styles from './styles';
+import { DefaultLayout } from '../Layouts';
+import { InputField } from '../InputField';
+import { SelectField } from '../SelectField';
+import { SelectPickerField } from '../SelectPickerField';
+import { colors } from '../../styles/colors';
+import { DatePickerField } from '../DatePickerField';
+import { BUTTON_TYPE } from '../../api/consts';
+import { CtButton } from '../Button';
+import Lng from '../../api/lang/i18n';
+
+type IProps = {
+ visible: Boolean,
+ onToggle: Function,
+ headerProps: Object,
+ inputFields: Object,
+ dropdownFields: Object,
+ selectFields: Object,
+ datePickerFields: Object,
+ language: String,
+};
+
+
+export class Filter extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ visible: false,
+ counter: 0,
+ };
+ }
+
+
+ inputField = (fields) => {
+ return fields.map((field, index) => {
+ const { name, hint, inputProps } = field
+ return (
+
+
+
+ )
+ })
+ }
+
+
+ selectField = (fields) => {
+ const { counter } = this.state
+
+ return fields.map((field, index) => {
+ const { name, items } = field
+
+ return (
+
+ 0 ? false : true}
+ {...field}
+ />
+
+ )
+ })
+ }
+
+ dropdownField = (fields) => {
+ return fields.map((field, index) => {
+ const { name, items } = field
+
+ return (
+
+
+
+ )
+ })
+ }
+
+ datePickerField = (fields) => {
+
+ return fields.map((field, index) => {
+ const { name } = field
+
+ return (
+
+
+
+ )
+ })
+ }
+
+ setFormField = (field, value) => {
+ const { form, dispatch } = this.props.clearFilter
+ dispatch(change(form, field, value));
+ };
+
+ onToggleFilter = () => {
+ this.setState((prevState) => {
+ return { visible: !prevState.visible }
+ });
+ }
+
+ onSubmit = (val) => {
+
+
+ let counter = 0
+ const { onSubmitFilter } = this.props
+
+ for (key in val) {
+ !(key === 'search') && counter++
+ }
+
+ this.setState({ counter })
+
+ this.onToggleFilter()
+
+ onSubmitFilter && onSubmitFilter()
+ }
+
+ onClear = () => {
+
+ const { clearFilter } = this.props
+ const { form, dispatch, formValues: { search } } = clearFilter
+
+ dispatch(reset(form));
+ dispatch(change(form, 'search', search));
+ }
+
+
+ BOTTOM_ACTION = () => {
+
+ const {
+ language,
+ clearFilter: { handleSubmit }
+ } = this.props
+
+ return (
+
+ this.onClear()}
+ btnTitle={Lng.t("button.clear", { locale: language })}
+ type={BUTTON_TYPE.OUTLINE}
+ containerStyle={styles.handleBtn}
+ buttonContainerStyle={styles.buttonContainer}
+ />
+
+
+
+ )
+ }
+
+ render() {
+
+ const {
+ headerProps,
+ inputFields,
+ dropdownFields,
+ selectFields,
+ datePickerFields,
+ language,
+ clearFilter: { handleSubmit }
+ } = this.props
+
+ const { visible, counter } = this.state
+
+ return (
+
+ this.onToggleFilter()}
+ activeOpacity={0.4}
+ >
+
+
+ {counter > 0 && (
+
+
+ {counter}
+
+
+ )}
+
+
+
+
+ this.onToggleFilter()}
+ hardwareAccelerated={true}
+ >
+
+
+ this.onToggleFilter(),
+ rightIconPress: handleSubmit(this.onSubmit),
+ ...headerProps
+ }}
+ bottomAction={this.BOTTOM_ACTION()}
+ >
+
+
+ {selectFields &&
+ this.selectField(selectFields)
+ }
+
+
+
+ {datePickerFields &&
+ this.datePickerField(datePickerFields)
+ }
+
+
+ {dropdownFields &&
+ this.dropdownField(dropdownFields)
+ }
+
+ {inputFields &&
+ this.inputField(inputFields)
+ }
+
+
+
+
+
+
+ );
+ }
+}
+
diff --git a/src/components/Filter/styles.js b/src/components/Filter/styles.js
new file mode 100644
index 00000000..cbddd9fe
--- /dev/null
+++ b/src/components/Filter/styles.js
@@ -0,0 +1,73 @@
+import { Platform, StyleSheet } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+import { isIPhoneX } from '../../api/helper';
+
+export default StyleSheet.create({
+ listViewContainer: {
+ flex: 1,
+ paddingBottom: isIPhoneX() ? 30 : 13,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ modalContainer: {
+ flex: 1,
+ ...Platform.select({
+ android: {
+ marginTop: -20,
+ margin: 0,
+ padding: 0
+ },
+ }),
+ },
+ backIcon: {
+ color: colors.dark,
+ },
+ submitHint: {
+ fontSize: 17,
+ },
+ dateFieldContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ marginTop: 5,
+ marginHorizontal: -10,
+ },
+ dateField: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingHorizontal: 6,
+ },
+ submitButton: {
+ flexDirection: "row",
+ // justifyContent: "space-between",
+ },
+ handleBtn: {
+ marginHorizontal: 9,
+ },
+ buttonContainer: {
+ flex: 1,
+ },
+ inputIconStyle: {
+ marginLeft: 5,
+ },
+ counter: {
+ position: "absolute",
+ top: -9,
+ right: -11,
+ width: 20,
+ height: 20,
+ borderRadius: 20 / 2,
+ backgroundColor: colors.primary,
+ borderWidth: 1.5,
+ borderColor: colors.veryLightGray,
+ alignItems: "center",
+ justifyContent: "center"
+ },
+ counterText: {
+ color: colors.veryLightGray,
+ fontSize: 13,
+ textAlign: "center",
+ }
+});
diff --git a/src/components/GradientBackground/index.js b/src/components/GradientBackground/index.js
new file mode 100644
index 00000000..a5d18aaf
--- /dev/null
+++ b/src/components/GradientBackground/index.js
@@ -0,0 +1,21 @@
+// @flow
+
+import React from 'react';
+import { Image, View } from 'react-native';
+import { LinearGradient } from 'expo-linear-gradient';
+import { colors } from '../../styles/colors';
+
+type IProps = {
+ children: any,
+};
+
+export const GradientBackground = ({ children }: IProps) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/components/GradientBackground/styles.js b/src/components/GradientBackground/styles.js
new file mode 100644
index 00000000..ab00ec9a
--- /dev/null
+++ b/src/components/GradientBackground/styles.js
@@ -0,0 +1,3 @@
+import { StyleSheet } from 'react-native';
+
+export const styles = StyleSheet.create({});
diff --git a/src/components/Header/index.js b/src/components/Header/index.js
new file mode 100644
index 00000000..05b7337d
--- /dev/null
+++ b/src/components/Header/index.js
@@ -0,0 +1,179 @@
+// @flow
+
+import React from 'react';
+import { View, TouchableOpacity, Alert } from 'react-native';
+import { Header, Text } from 'react-native-elements';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import styles from './styles';
+import { colors } from '../../styles/colors';
+import { Filter } from '../Filter';
+
+type IProps = {
+ leftIcon: String,
+ leftIconPress: Function,
+ title: String,
+ rightIcon: String,
+ rightIconPress: Function,
+ placement: String,
+ transparent: Boolean,
+ rightIconHint: String,
+ titleStyle: Object,
+ leftIconStyle: Object,
+ noBorder: Boolean,
+ hasCircle: Boolean,
+ rightIconProps: Object,
+ rightComponent: any,
+ rightIconHintStyle: Object,
+ titleOnPress: Object
+};
+
+export const CtHeader = ({
+ leftIcon,
+ leftIconPress,
+ title,
+ rightIcon,
+ rightIconPress,
+ placement,
+ transparent,
+ rightIconHint,
+ titleStyle,
+ leftIconStyle,
+ hasCircle,
+ noBorder,
+ rightIconProps,
+ rightComponent,
+ rightIconHintStyle,
+ filterProps,
+ titleOnPress,
+}: IProps) => {
+ const hederTitle = {
+ text: title,
+ style: [
+ {
+ color: transparent ? colors.dark2 : colors.white,
+ },
+ styles.title,
+ titleStyle && titleStyle,
+ ],
+ }
+
+ const displayTitle = () => {
+
+ if (titleOnPress) {
+ return (
+ titleOnPress()}
+ >
+
+ {title}
+
+
+ )
+ }
+
+ return leftIcon && hederTitle
+ }
+
+ return (
+
+
+
+ ) : hederTitle
+ }
+ centerComponent={displayTitle()}
+ rightComponent={!rightComponent ? (
+
+ {filterProps && (
+
+
+
+ )}
+
+
+
+
+ {rightIconHint && (
+
+ {rightIconHint}
+
+ )}
+
+
+
+ {rightIconPress && !rightIconHint && (
+
+ )}
+
+
+
+
+
+ ) : rightComponent
+ }
+ containerStyle={[
+ styles.containerStyle,
+ transparent && styles.transparent,
+ noBorder && styles.borderBottom
+ ]}
+ statusBarProps={{
+ barStyle: 'dark-content',
+ animated: true,
+ translucent: true
+ }}
+ />
+ );
+};
diff --git a/src/components/Header/styles.js b/src/components/Header/styles.js
new file mode 100644
index 00000000..2d251723
--- /dev/null
+++ b/src/components/Header/styles.js
@@ -0,0 +1,49 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+
+export default StyleSheet.create({
+ transparent: {
+ backgroundColor: 'transparent',
+ },
+ containerStyle: {
+ borderBottomWidth: 1,
+ borderColor: colors.darkGray,
+ backgroundColor: colors.white,
+ marginTop: 8,
+ },
+ borderBottom: {
+ borderBottomWidth: 0,
+ },
+ title: {
+ fontSize: 17,
+ color: colors.dark,
+ fontFamily: fonts.poppinsMedium,
+ marginLeft: 10,
+ },
+ rightBtn: {
+ flexDirection: 'row',
+ marginRight: 10,
+ },
+ hasCircle: {
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ height: 28,
+ width: 28,
+ borderRadius: 16,
+ backgroundColor: colors.primary
+ },
+ rightBtnTitle: {
+ paddingRight: 10,
+ paddingTop: 3,
+ color: colors.primary,
+ },
+ rightContainer: {
+ flexDirection: 'row',
+ },
+ filterColumn: {
+ marginRight: 13,
+ marginTop: 3,
+ }
+});
diff --git a/src/components/InfiniteScroll/index.js b/src/components/InfiniteScroll/index.js
new file mode 100644
index 00000000..4d3fb147
--- /dev/null
+++ b/src/components/InfiniteScroll/index.js
@@ -0,0 +1,110 @@
+// @flow
+
+import React from 'react';
+import { ScrollView, RefreshControl, ActivityIndicator, View, Text } from 'react-native';
+
+import { styles } from './styles';
+import { colors } from '../../styles/colors';
+
+type IProps = {
+ loading: boolean,
+ loaderHeight: Number,
+ children: any,
+ canLoadMore: boolean,
+ isEmpty: boolean,
+ onEndReached: Function,
+ onPullToRefresh: Function,
+ refreshControlColor: string,
+ contentContainerStyle: Object,
+ style: Object,
+};
+
+type IState = {
+ refreshing: boolean,
+};
+
+const isCloseToBottom = ({ layoutMeasurement, contentOffset, contentSize }) => {
+ const paddingToBottom = 75;
+
+ return layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom;
+};
+
+export class InfiniteScroll extends React.Component {
+ state = {
+ refreshing: false,
+ };
+
+ _onRefresh = () => {
+ const { refreshing } = this.state;
+ const { onPullToRefresh } = this.props;
+
+ // check if promise
+ if (onPullToRefresh && !refreshing) {
+ this.setState({
+ refreshing: true,
+ });
+
+ onPullToRefresh(() => {
+ this.setState({
+ refreshing: false,
+ });
+ });
+ }
+ };
+
+ render() {
+ const {
+ loading,
+ children,
+ canLoadMore,
+ onEndReached,
+ onPullToRefresh,
+ isEmpty,
+ refreshControlColor,
+ contentContainerStyle,
+ style,
+ loaderHeight,
+ size = 'small',
+ } = this.props;
+ const { refreshing } = this.state;
+
+ return (
+ {
+ if (isCloseToBottom(nativeEvent) && !loading) {
+ if (typeof onEndReached === 'function' && canLoadMore && !refreshing) {
+ onEndReached();
+ }
+ }
+ }}
+ scrollEventThrottle={400}
+ refreshControl={
+ onPullToRefresh ? (
+
+ ) : null
+ }
+ showsVerticalScrollIndicator={false}
+ >
+ {children}
+
+ {(!isEmpty && canLoadMore && loading) && (
+
+ )}
+
+ );
+ }
+}
diff --git a/src/components/InfiniteScroll/styles.js b/src/components/InfiniteScroll/styles.js
new file mode 100644
index 00000000..cc52d1ed
--- /dev/null
+++ b/src/components/InfiniteScroll/styles.js
@@ -0,0 +1,9 @@
+import { StyleSheet } from 'react-native';
+
+export const styles = StyleSheet.create({
+ container: {
+ position: 'relative',
+ flex: 1,
+ height: '100%',
+ },
+});
diff --git a/src/components/InputField/index.js b/src/components/InputField/index.js
new file mode 100644
index 00000000..d0b70fa8
--- /dev/null
+++ b/src/components/InputField/index.js
@@ -0,0 +1,227 @@
+// @flow
+
+import React, { Component } from 'react';
+import { View, TouchableOpacity, Text } from 'react-native';
+import { connect } from 'react-redux';
+import { Input } from 'react-native-elements';
+import styles from './styles';
+import { IInputField } from './type';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { colors } from '../../styles/colors';
+import Lng from '../../api/lang/i18n';
+
+export class InputFieldComponent extends Component {
+ inputRef = {};
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ isSecureTextEntry: this.props.secureTextEntry,
+ active: false,
+ inputHeight: 0,
+ isOptionsVisible: false,
+ };
+ }
+
+ toggleSecureTextEntry = () => {
+ this.inputRef.blur();
+ this.setState((prevState) => ({ isSecureTextEntry: !prevState.isSecureTextEntry }));
+
+ setTimeout(() => {
+ this.inputRef.focus();
+ }, 100);
+ };
+
+ getSign = () => {
+ const { dollarField, percentageField } = this.props;
+
+ if (dollarField) {
+ return '$';
+ }
+
+ if (percentageField) {
+ return '%';
+ }
+
+ return null;
+ };
+
+ saveInputHeight = (event) => {
+ const { height } = event.nativeEvent.layout;
+
+ this.setState({ inputHeight: height });
+ };
+
+ render() {
+ const {
+ input: { onChange, onBlur, onFocus, value },
+ hint,
+ meta: { error, submitFailed },
+ secureTextEntry,
+ refLinkFn,
+ tip,
+ inputContainerStyle,
+ onChangeText,
+ editable = true,
+ hideError,
+ autocomplete,
+ options,
+ disabled,
+ textColor,
+ height,
+ setActivity,
+ errorNumberOfLines,
+ fieldStyle,
+ hintStyle,
+ containerStyle,
+ leftIcon,
+ leftIconSolid = false,
+ textStyle,
+ validationStyle,
+ inputProps,
+ rounded,
+ isCurrencyInput = false,
+ leftIconStyle,
+ language,
+ maxNumber = 0,
+ isRequired = false,
+ secureTextIconContainerStyle,
+ } = this.props;
+ const { isSecureTextEntry, active, inputHeight, isOptionsVisible } = this.state;
+ const sign = this.getSign();
+ const isOptions = autocomplete && isOptionsVisible && !!options.length;
+
+ return (
+
+ {hint && (
+
+ {hint}
+ {isRequired && (
+ *
+ )}
+
+ )}
+
+
+
+
+ )
+ }
+ leftIconContainerStyle={leftIcon &&
+ [styles.leftIcon, leftIconStyle && leftIconStyle]
+ }
+ inputStyle={[
+ styles.input,
+ active && styles.activeInput,
+ textColor && { color: textColor },
+ textStyle && textStyle,
+ height && { height },
+ inputProps.multiline && styles.multilineField
+ ]}
+ inputContainerStyle={[
+ styles.inputContainerStyle,
+ secureTextEntry && styles.inputPassword,
+ inputContainerStyle && inputContainerStyle,
+ rounded && { borderRadius: 5 },
+ disabled && styles.disabledInput,
+ submitFailed && error && styles.inputError,
+ ]}
+ {...inputProps}
+ onChangeText={(enteredValue: string) => {
+ onChangeText && onChangeText(enteredValue);
+ isCurrencyInput ?
+ onChange(enteredValue * 100) : onChange(enteredValue);
+
+ setTimeout(() => {
+ if (this.inputRef) {
+ this.inputRef.setNativeProps({ text: enteredValue });
+ this.inputRef.value = isCurrencyInput ?
+ (enteredValue * 100) : enteredValue;
+ }
+ });
+ }}
+ onFocus={(event) => {
+ this.setState({
+ active: true,
+ isOptionsVisible: true,
+ });
+ setActivity && setActivity(true);
+ if (onFocus) {
+ onFocus(event);
+ }
+ }}
+ value={value && `${isCurrencyInput ? (value / 100) : value}`}
+ secureTextEntry={isSecureTextEntry}
+ ref={(ref) => {
+ this.inputRef = ref;
+ refLinkFn && refLinkFn(ref);
+ }}
+ placeholderTextColor={colors.darkGray}
+ editable={editable && !disabled}
+ allowFontScaling={false}
+ textAlignVertical={inputProps && inputProps.multiline && 'top'}
+ />
+
+ {sign && (
+
+ {sign}
+
+ )}
+ {secureTextEntry && (
+
+
+
+ )}
+ {!hideError && submitFailed && error && (
+
+
+ {Lng.t(error, { locale: language, hint, maxNumber })}
+
+
+ )}
+ {!(submitFailed && error) && !isOptions && tip && (
+
+ {tip}
+
+ )}
+
+
+ );
+ }
+}
+
+const mapStateToProps = ({ global }) => ({
+ language: global.language,
+});
+
+const mapDispatchToProps = {};
+
+export const InputField = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(InputFieldComponent);
diff --git a/src/components/InputField/styles.js b/src/components/InputField/styles.js
new file mode 100644
index 00000000..00a58d44
--- /dev/null
+++ b/src/components/InputField/styles.js
@@ -0,0 +1,127 @@
+import { StyleSheet, Platform } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+
+export default StyleSheet.create({
+ hint: {
+ color: colors.secondary,
+ fontSize: 14,
+ fontFamily: fonts.poppinsMedium,
+ },
+ hintFocused: {
+ opacity: 1,
+ },
+ required: {
+ color: colors.danger
+ },
+ inputFieldWrapper: {
+ flexShrink: 0,
+ marginVertical: 10,
+ },
+ inputWrapper: {
+ flexShrink: 0,
+ position: 'relative',
+ marginTop: 6,
+ },
+ input: {
+ color: colors.dark2,
+ fontSize: 16,
+ fontFamily: fonts.poppins,
+ height: 40,
+ ...Platform.select({
+ android: {
+ height: 45,
+ },
+ }),
+ },
+ multilineField: {
+ paddingHorizontal: 2,
+ paddingVertical: 7,
+ },
+ inputContainerStyle: {
+ backgroundColor: 'white',
+ borderWidth: 1,
+ borderColor: colors.lightGray,
+ paddingLeft: 10,
+ },
+ containerStyle: {
+ paddingHorizontal: 0,
+ },
+ inputPassword: {
+ paddingRight: 30,
+ },
+ inputError: {
+ borderColor: colors.dangerLight
+ },
+ validation: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ paddingVertical: 2,
+ paddingHorizontal: 5,
+ borderRadius: 2,
+ overflow: 'hidden',
+ flex: 1,
+ zIndex: 100,
+ backgroundColor: colors.danger,
+ },
+ icon: {
+ position: 'absolute',
+ top: 10,
+ right: 10,
+ width: 33,
+ height: 33,
+ justifyContent: "center",
+ alignItems: "center",
+ },
+ leftIcon: {
+ marginLeft: 5,
+ marginRight: 10,
+ },
+ activeHint: {
+ color: colors.white,
+ },
+ activeInput: {
+ // borderColor: colors.white,
+ },
+ inputTip: {
+ color: colors.white,
+ opacity: 0.5,
+ position: 'absolute',
+ top: 38,
+ left: 0,
+ right: 0,
+ },
+ dollarFieldInput: {
+ paddingLeft: 15,
+ },
+ percentageFieldInput: {
+ paddingLeft: 22,
+ },
+ signField: {
+ position: 'absolute',
+ top: 4,
+ left: 0,
+ },
+ optionsList: {
+ // maxHeight: 180,
+ },
+ disabledInput: {
+ backgroundColor: colors.lightGray,
+ opacity: 0.5
+ },
+ lightThemeDisabledInput: {
+ color: colors.gray6,
+ },
+ pointIcon: {
+ position: 'absolute',
+ top: 3,
+ },
+ value: {
+ color: colors.danger,
+ fontWeight: '600',
+ },
+ additionalValue: {
+ opacity: 0.7,
+ },
+});
diff --git a/src/components/InputField/type.js b/src/components/InputField/type.js
new file mode 100644
index 00000000..4160d37f
--- /dev/null
+++ b/src/components/InputField/type.js
@@ -0,0 +1,62 @@
+// @flow
+
+export type IInputField = {
+ input: {
+ onChange: Function,
+ onBlur: Function,
+ onFocus: Function,
+ value: string,
+ },
+ meta: {
+ error: string,
+ submitFailed: Boolean,
+ },
+ options: Array,
+
+ onSubmitEditing: Function,
+ onChangeText: Function,
+ onSelectOption: Function,
+ setActivity: Function,
+ refLinkFn: Function,
+
+ dollarField: Boolean,
+ percentageField: Boolean,
+ editable: Boolean,
+ hideError: Boolean,
+ autocomplete: Boolean,
+ applyOptionValue: Boolean,
+ disabled: Boolean,
+ errorNumberOfLines: Boolean,
+ isRequired: Boolean,
+ rounded: Boolean,
+ isCurrencyInput: Boolean,
+ secureTextEntry: Boolean,
+ lightTheme: Boolean,
+ multiline: Boolean,
+ autoFocus: Boolean,
+
+ height: Number,
+ maxLength: number,
+
+ textStyle: Object,
+ validationStyle: Object,
+ inputProps: Object,
+ leftIconStyle: Object,
+ fieldStyle: Object,
+ hintStyle: Object,
+ containerStyle: Object,
+ inputContainerStyle: Object,
+ inputStyle: Object,
+ secureTextIconContainerStyle: Object,
+
+ keyboardType: string,
+ textContentType: string,
+ tip: string,
+ placeholder: string,
+ hint: string,
+ leftIcon: String,
+ language: String,
+ textColor: String,
+ returnKeyType: string,
+ autoCapitalize: string
+};
diff --git a/src/components/Layouts/default/index.js b/src/components/Layouts/default/index.js
new file mode 100644
index 00000000..cfba21cb
--- /dev/null
+++ b/src/components/Layouts/default/index.js
@@ -0,0 +1,77 @@
+// @flow
+
+import React from 'react';
+import { View, KeyboardAvoidingView, ScrollView } from 'react-native';
+import { styles } from './styles';
+import { Field } from 'redux-form';
+import { InputField, CtHeader, CtDivider } from '../..';
+import { NavigationEvents } from 'react-navigation';
+import { Content } from '../../Content';
+import Dropdown from '../../Dropdown';
+import Toast from '../../Toast';
+
+type IProps = {
+ children: Object,
+ headerProps: Object,
+ hasRightButton: Boolean,
+ rightIcon: String,
+ bottomAction: any,
+ loadingProps: Object,
+ dropdownProps: Object,
+ toastProps: Object
+};
+
+export const DefaultLayout = ({
+ children,
+ headerProps,
+ rightIcon,
+ bottomAction,
+ onFocus,
+ loadingProps,
+ dropdownProps,
+ toastProps
+}: IProps) => {
+ return (
+
+
+ {toastProps && ( )}
+
+
+
+
+ )}
+ />
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+ {bottomAction && (
+
+ {bottomAction}
+
+ )}
+
+ );
+};
diff --git a/src/components/Layouts/default/styles.js b/src/components/Layouts/default/styles.js
new file mode 100644
index 00000000..11d7ab15
--- /dev/null
+++ b/src/components/Layouts/default/styles.js
@@ -0,0 +1,42 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../styles/colors';
+import { fonts } from '../../../styles/fonts';
+import { definePlatformParam, isIPhoneX, headerTitle } from '../../../api/helper';
+
+export const styles = StyleSheet.create({
+ page: {
+ position: 'relative',
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ content: {
+ flex: 1,
+ paddingTop: definePlatformParam(0, 16),
+ marginBottom: isIPhoneX() ? 82 : 52,
+ },
+ bottomView: {
+ backgroundColor: colors.white,
+ paddingVertical: 15,
+ paddingHorizontal: 10,
+ paddingBottom: isIPhoneX() ? 35 : 13,
+ borderTopWidth: 1,
+ borderColor: colors.lightGray,
+ },
+ headerTitleStyle: {
+ fontSize: 17,
+ color: colors.dark1,
+ fontFamily: fonts.poppins,
+ ...headerTitle({})
+ },
+ inputField: {
+ paddingHorizontal: 19,
+ paddingVertical: 0,
+ marginVertical: 8,
+ },
+ searchFieldContainer: {
+ // backgroundColor: colors.veryLightGray,
+ paddingVertical: 10,
+ },
+});
diff --git a/src/components/Layouts/index.js b/src/components/Layouts/index.js
new file mode 100644
index 00000000..f4e50ee6
--- /dev/null
+++ b/src/components/Layouts/index.js
@@ -0,0 +1,2 @@
+export * from './main'
+export * from './default'
diff --git a/src/components/Layouts/main/index.js b/src/components/Layouts/main/index.js
new file mode 100644
index 00000000..83f78d13
--- /dev/null
+++ b/src/components/Layouts/main/index.js
@@ -0,0 +1,109 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import { connect } from 'react-redux';
+import { Field } from 'redux-form';
+import { NavigationEvents } from 'react-navigation';
+import { styles } from './styles';
+import { InputField, CtHeader, CtDivider } from '../..';
+import { Content } from '../../Content';
+import Lng from '../../../api/lang/i18n';
+
+type IProps = {
+ children: Object,
+ headerProps: Object,
+ onSearch: Function,
+ bottomDivider: Boolean,
+ hasSearchField: Boolean,
+ onToggleFilter: Function,
+ filterProps: Object,
+ inputProps: Object,
+ dividerStyle: Object,
+ loadingProps: Object
+};
+
+const MainLayoutComponent = ({
+ children,
+ headerProps,
+ onSearch,
+ bottomDivider,
+ hasSearchField = true,
+ onFocus,
+ bottomAction,
+ filter = false,
+ filterProps,
+ inputProps,
+ dividerStyle,
+ loadingProps,
+ language
+}: IProps) => {
+
+ let hasFilter = filter ? { ...filterProps } : null
+
+ return (
+
+
+
+
+
+ {hasSearchField && (
+
+
+
+ )}
+
+ {bottomDivider &&
+
+ }
+
+
+ {children}
+
+
+
+
+ {bottomAction && (
+
+ {bottomAction}
+
+ )}
+
+ );
+};
+
+const mapStateToProps = ({ global }) => ({
+ language: global.language,
+});
+
+const mapDispatchToProps = {};
+
+// connect
+export const MainLayout = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(MainLayoutComponent);
diff --git a/src/components/Layouts/main/styles.js b/src/components/Layouts/main/styles.js
new file mode 100644
index 00000000..696d935e
--- /dev/null
+++ b/src/components/Layouts/main/styles.js
@@ -0,0 +1,46 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../styles/colors';
+import { fonts } from '../../../styles/fonts';
+import { definePlatformParam, isIPhoneX } from '../../../api/helper';
+
+export const styles = StyleSheet.create({
+ page: {
+ position: 'relative',
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ backgroundColor: colors.veryLightGray
+ },
+ content: {
+ flex: 1,
+ },
+ headerTitleStyle: {
+ fontSize: 30,
+ color: colors.dark2,
+ fontWeight: '600',
+ fontFamily: fonts.poppinsMedium
+ },
+ inputField: {
+ paddingHorizontal: 20,
+ paddingVertical: 0,
+ marginVertical: 8,
+ },
+ searchFieldContainer: {
+ paddingBottom: 5,
+ },
+ bottomView: {
+ backgroundColor: colors.white,
+ paddingVertical: 15,
+ paddingHorizontal: 10,
+ paddingBottom: isIPhoneX() ? 35 : 13,
+ borderTopWidth: 1,
+ borderColor: colors.lightGray,
+ },
+ columnSearch: {
+ flex: 7,
+ },
+ columnIcon: {
+ flex: 1,
+ justifyContent: "center",
+ },
+});
diff --git a/src/components/ListView/index.js b/src/components/ListView/index.js
new file mode 100644
index 00000000..1f617037
--- /dev/null
+++ b/src/components/ListView/index.js
@@ -0,0 +1,252 @@
+import React, { Component } from 'react';
+import { View, Text } from 'react-native';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { styles } from './styles';
+import { ListItem, Avatar, CheckBox } from 'react-native-elements';
+import { InfiniteScroll } from '../InfiniteScroll';
+import { Empty } from '../Empty';
+import { fonts } from '../../styles/fonts';
+import { colors } from '../../styles/colors';
+import { CurrencyFormat } from '../CurrencyFormat';
+
+type IProps = {
+ loading: Boolean,
+ onRefresh: Function,
+ refreshing: Boolean,
+ hasAvatar: Boolean,
+ getItems: Function,
+ getFreshItems: Function,
+ canLoadMore: Boolean,
+ isEmpty: Boolean,
+ containerStyle: Object,
+ emptyContentProps: Object,
+ rightTitleStyle: Object,
+ leftTitleStyle: Object,
+ leftSubTitleLabelStyle: Object,
+ listItemProps: Object,
+ backgroundColor: String,
+ compareField: String,
+ checkedItems: Array,
+ listViewContainerStyle: Object,
+};
+
+export class ListView extends Component {
+ constructor(props) {
+ super(props);
+ }
+
+ leftTitle = (title) => {
+ const { leftTitleStyle } = this.props;
+ return (
+
+ {title}
+
+ );
+ };
+
+ getCheckedItem = (item) => {
+ const { compareField, checkedItems, valueCompareField } = this.props
+
+ if (checkedItems) {
+ return checkedItems.filter(
+ val => val[valueCompareField] === item.fullItem[compareField]
+ ).length > 0
+ }
+
+
+ return false
+ }
+
+ leftSubTitle = ({ title, label, labelBgColor, labelTextColor, labelComponent } = {}) => {
+ const { leftSubTitleLabelStyle, leftSubTitleStyle } = this.props;
+ if (!title && !label && !labelComponent) {
+ return
+ }
+
+ return (
+
+
+ {title}
+
+
+ {(label || labelComponent) && (
+
+
+ {labelComponent ? labelComponent : (
+
+ {label}
+
+ )
+ }
+
+
+ )}
+
+ );
+ };
+
+ itemsList = (item, index) => {
+ const {
+ onPress,
+ bottomDivider = false,
+ containerStyle,
+ rightTitleStyle,
+ backgroundColor,
+ itemContainer,
+ listItemProps,
+ hasCheckbox,
+ contentContainerStyle,
+ } = this.props;
+ return (
+
+ ) : item.rightTitle
+ }
+ rightSubtitle={item.rightSubtitle}
+ bottomDivider={bottomDivider}
+ rightTitleStyle={[
+ styles.rightTitle,
+ rightTitleStyle && rightTitleStyle
+ ]}
+ rightSubtitleStyle={styles.rightSubTitle}
+ contentContainerStyle={[
+ styles.contentContainer,
+ contentContainerStyle
+ ]}
+ rightContentContainerStyle={styles.rightContentContainer}
+ containerStyle={[
+ styles.containerStyle,
+ {
+ backgroundColor:
+ (backgroundColor && backgroundColor) || colors.veryLightGray,
+ },
+ itemContainer && itemContainer,
+ ]}
+ leftElement={hasCheckbox && (
+ onPress(item.fullItem)}
+ />
+ )}
+ fontFamily={fonts.poppins}
+ onPress={() => onPress(item.fullItem)}
+ {...listItemProps}
+ />
+ );
+ };
+
+ itemsWithAvatar = (item, index) => {
+ const { onPress, bottomDivider = false, itemContainer, listItemProps, leftIconStyle } = this.props;
+ const { title, subtitle, fullItem, leftAvatar, leftIcon, leftIconSolid = false, iconSize = 22 } = item
+
+ return (
+ onPress(fullItem)}
+ leftAvatar={
+ leftAvatar ? (
+
+ ) : (
+
+ )}
+ {...listItemProps}
+ />
+ );
+ };
+
+ render() {
+ const {
+ items,
+ loading = false,
+ refreshing = true,
+ hasAvatar = false,
+ getItems,
+ getFreshItems,
+ canLoadMore,
+ isEmpty,
+ containerStyle,
+ emptyContentProps,
+ listViewContainerStyle,
+ } = this.props;
+
+ return (
+ onHide && onHide() : getFreshItems}
+ >
+ {!isEmpty
+ ? items.map((item, index) =>
+ !hasAvatar
+ ? this.itemsList(item, index)
+ : this.itemsWithAvatar(item, index),
+ )
+ : !loading && }
+
+ );
+ }
+}
+
+export default ListView;
diff --git a/src/components/ListView/styles.js b/src/components/ListView/styles.js
new file mode 100644
index 00000000..06830dac
--- /dev/null
+++ b/src/components/ListView/styles.js
@@ -0,0 +1,82 @@
+import { StyleSheet } from 'react-native';
+import { fonts } from '../../styles/fonts';
+import { colors } from '../../styles/colors';
+
+export const styles = StyleSheet.create({
+ leftTitle: {
+ fontSize: 18,
+ fontFamily: fonts.poppinsLight,
+ },
+ rightTitle: {
+ fontSize: 18,
+ color: colors.dark2,
+ fontWeight: '500',
+ fontFamily: fonts.poppinsSemiBold,
+ },
+ containerStyle: {
+ alignItems: 'flex-start',
+ paddingVertical: 13,
+ paddingHorizontal: 20,
+ },
+ containerWithAvatar: {
+ paddingVertical: 13,
+ paddingHorizontal: 20,
+ },
+ checkboxContainerStyle: {
+ padding: 0,
+ margin: 0,
+ paddingTop: 5,
+ marginHorizontal: 0,
+ paddingHorizontal: 0
+ },
+ checkboxContainerPadding: {
+ paddingTop: 12,
+ },
+ rightSubTitle: {
+ fontSize: 13,
+ color: '#8e95ac',
+ },
+ leftSubTitleText: {
+ fontSize: 14,
+ color: colors.darkGray,
+ },
+ leftSubTitleLabelContainer: {
+ display: 'flex',
+ alignItems: 'flex-start',
+ marginTop: 6,
+ },
+ leftSubTitleLabel: {
+ textTransform: 'uppercase',
+ fontFamily: fonts.poppins,
+ fontSize: 12,
+ },
+ success: {
+ backgroundColor: colors.successLight2,
+ color: colors.successDark,
+ },
+ warning: {
+ backgroundColor: colors.warningLight,
+ color: colors.warningDark,
+ },
+ danger: {
+ backgroundColor: colors.dangerLight,
+ color: colors.dangerDark,
+ },
+ labelInnerContainerStyle: {
+ paddingVertical: 4,
+ paddingHorizontal: 8,
+ borderRadius: 1,
+ },
+ leftSubTitleContainer: {
+ paddingLeft: 1,
+ },
+ container: {
+ flex: 1,
+ },
+ contentContainer: {
+ flex: 1,
+ },
+ rightContentContainer: {
+ flex: 1,
+ }
+});
diff --git a/src/components/Loading/index.js b/src/components/Loading/index.js
new file mode 100644
index 00000000..3bfb9f9d
--- /dev/null
+++ b/src/components/Loading/index.js
@@ -0,0 +1,15 @@
+// @flow
+
+import React from 'react';
+import { ActivityIndicator } from 'react-native';
+import { colors } from '../../styles/colors';
+
+type IProps = {
+ size: 'small' | 'large',
+ color: string,
+ style: Object,
+};
+
+export const Loading = ({ size = 'small', color = colors.veryDarkGray, style }: IProps) => (
+
+);
diff --git a/src/components/LostConnection/index.js b/src/components/LostConnection/index.js
new file mode 100644
index 00000000..1306a821
--- /dev/null
+++ b/src/components/LostConnection/index.js
@@ -0,0 +1,107 @@
+import React, { Component } from 'react'
+import { View } from 'react-native'
+import { connect } from 'react-redux';
+import styles from './styles';
+import { AssetImage } from '../AssetImage';
+import { CtGradientButton } from '../Button';
+import { Text } from 'react-native-elements';
+import { IMAGES } from '../../config';
+import Lng from '../../api/lang/i18n';
+import { goBack, MOUNT, UNMOUNT } from '../../navigation/actions';
+import { checkConnection } from '../../api/helper';
+import { ROUTES } from '../../navigation/routes';
+
+export class LostConnection extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ loading: false
+ };
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation, ROUTES.LOST_CONNECTION)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+
+ onRetry = async () => {
+
+ this.setState({ loading: true })
+
+ setTimeout(() => {
+ this.setState({ loading: false })
+ }, 1000);
+
+ const { navigation } = this.props
+
+ let isConnected = await checkConnection()
+ !isConnected ? navigation.navigate(ROUTES.LOST_CONNECTION) :
+ navigation.pop();
+ }
+
+ render() {
+
+ const { language } = this.props;
+ const { loading } = this.state
+
+ return (
+
+
+
+
+
+
+ {Lng.t("lostInternet.title", { locale: language })}
+
+
+
+
+
+
+
+ {Lng.t("lostInternet.description", { locale: language })}
+
+
+
+
+ this.onRetry()}
+ btnTitle={Lng.t("button.retry", { locale: language })}
+ loading={loading}
+ />
+
+
+
+
+
+ )
+ }
+}
+
+const mapStateToProps = ({ global }) => ({
+ language: global.language,
+});
+
+const mapDispatchToProps = {};
+
+// connect
+const LostConnectionContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(LostConnection);
+
+LostConnectionContainer.navigationOptions = () => ({
+ header: null,
+});
+
+
+export default LostConnectionContainer;
diff --git a/src/components/LostConnection/styles.js b/src/components/LostConnection/styles.js
new file mode 100644
index 00000000..9fc6458b
--- /dev/null
+++ b/src/components/LostConnection/styles.js
@@ -0,0 +1,49 @@
+import { StyleSheet, Dimensions } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+
+
+const { width, height } = Dimensions.get('window');
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'flex-start',
+ display: 'flex',
+ backgroundColor: colors.veryLightGray
+ },
+ main: {
+ flex: 1,
+ flexDirection: 'column',
+ paddingHorizontal: 25,
+ justifyContent: 'center',
+ },
+ logoContainer: {
+ },
+ imgLogo: {
+ width: width,
+ height: 140,
+ resizeMode: 'cover',
+ },
+ bodyContainer: {
+ textAlign: "center",
+ alignItems: "center",
+ },
+ title: {
+ marginBottom: 25,
+ color: colors.secondary,
+ fontFamily: fonts.poppinsSemiBold,
+ fontSize: 22,
+ },
+ internetIcon: {
+ marginTop: 15,
+ marginBottom: 10
+ },
+ description: {
+ marginTop: 50,
+ color: colors.veryDarkGray,
+ fontFamily: fonts.poppinsLight,
+ textAlign: "center",
+ fontSize: 13,
+ },
+});
diff --git a/src/components/RadioButtonGroup/index.js b/src/components/RadioButtonGroup/index.js
new file mode 100644
index 00000000..73d48037
--- /dev/null
+++ b/src/components/RadioButtonGroup/index.js
@@ -0,0 +1,73 @@
+import React, { Component } from 'react';
+import { TouchableOpacity, View, Text } from 'react-native';
+import { FakeInput } from '../FakeInput';
+import { styles } from './styles';
+
+type IProps = {
+ input: Object,
+ disabled: Boolean,
+ fakeInputContainerStyle: Object,
+ hint: String,
+ initialValue: string,
+ onChangeCallback: Function
+};
+export class RadioButtonGroup extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ selected: false,
+ };
+ }
+
+ componentWillMount() {
+ const { initialValue } = this.props
+ if (initialValue) {
+ this.setState({
+ selected: initialValue
+ })
+ }
+
+ }
+
+ onSelect = (val) => {
+ const { input: { onChange }, onChangeCallback } = this.props
+
+ onChange(val)
+ onChangeCallback && onChangeCallback(val)
+ this.setState({ selected: val })
+ }
+
+ render() {
+ const { options, hint } = this.props;
+ const { selected } = this.state;
+
+ return (
+
+ {hint && (
+ {hint}
+ )}
+
+ {options.map(item => (
+ this.onSelect(item.key)}
+ >
+
+
+
+
+ {item.label}
+
+
+ ))}
+
+
+ );
+ }
+}
+
diff --git a/src/components/RadioButtonGroup/styles.js b/src/components/RadioButtonGroup/styles.js
new file mode 100644
index 00000000..98fedd92
--- /dev/null
+++ b/src/components/RadioButtonGroup/styles.js
@@ -0,0 +1,51 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+
+export const styles = StyleSheet.create({
+ fieldContainer: {
+ marginTop: 15,
+ },
+ buttonGroupContainer: {
+ justifyContent: 'space-between',
+ flexDirection: 'row',
+ },
+ buttonContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 30,
+ },
+ label: {
+ color: colors.dark2,
+ fontFamily: fonts.poppins
+ },
+ hintStyle: {
+ color: colors.dark2,
+ fontFamily: fonts.poppins,
+ marginBottom: 15,
+ },
+ checkedLabel: {
+ color: colors.primary,
+ },
+ circle: {
+ height: 20,
+ width: 20,
+ borderRadius: 10,
+ borderWidth: 1,
+ borderColor: colors.lightGray,
+ backgroundColor: colors.white,
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginRight: 10,
+ },
+ checkedCircle: {
+ backgroundColor: colors.primary,
+ },
+ middleCircle: {
+ width: 6,
+ height: 6,
+ borderRadius: 3,
+ backgroundColor: colors.white,
+ },
+});
diff --git a/src/components/SelectField/index.js b/src/components/SelectField/index.js
new file mode 100644
index 00000000..67aea7bf
--- /dev/null
+++ b/src/components/SelectField/index.js
@@ -0,0 +1,547 @@
+// @flow
+
+import React, { Component } from 'react';
+import {
+ View,
+} from 'react-native';
+import styles from './styles';
+import { SlideModal, FakeInput } from '..';
+import { change } from 'redux-form';
+import { CtButton } from '../Button';
+import Lng from '../../api/lang/i18n';
+import { connect } from 'react-redux';
+import { IProps } from './type';
+import { headerTitle } from '../../api/helper';
+
+export class SelectFieldComponent extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ page: 1,
+ refreshing: false,
+ pagination: {
+ page: 1,
+ limit: 10,
+ lastPage: 1
+ },
+ search: '',
+ visible: false,
+ values: '',
+ selectedItems: [],
+ oldItems: [],
+ defaultItem: [],
+ searchItems: [],
+ oldValue: '',
+ };
+ }
+
+ componentDidMount() {
+ const {
+ input: { value },
+ pagination,
+ compareField,
+ items,
+ apiSearch,
+ displayName,
+ concurrentMultiSelect
+ } = this.props
+
+ if (typeof items !== 'undefined') {
+ let newValue = ''
+
+ for (const key in items) {
+ if (key !== 'undefined' && items[key]['fullItem'][compareField] === value) {
+ newValue = items[key]['fullItem'][displayName]
+ }
+ }
+ concurrentMultiSelect && this.setState({
+ selectedItems: value,
+ oldItems: value,
+ })
+
+ this.setState({
+ values: compareField ? newValue : value[displayName],
+ defaultItem: items || [],
+ searchItems: items || [],
+ oldValue: compareField ? value : value[displayName],
+ })
+
+ }
+
+ apiSearch && this.onGetItems({
+ fresh: true,
+ onResult: () => {
+ const {
+ items,
+ onSelect,
+ isMultiSelect,
+ valueCompareField,
+ compareField,
+ input: { onChange, value },
+ hasFirstItem = true
+ } = this.props
+
+ if (typeof items !== 'undefined' && items.length !== 0 && hasFirstItem) {
+
+ firstItem = items[0]['fullItem']
+
+ this.setState({
+ values: compareField && firstItem[displayName],
+ oldValue: compareField ? firstItem[compareField] : firstItem[displayName]
+ })
+
+ /* if (!value) {
+ if (!onSelect) {
+ isMultiSelect ?
+ onChange([
+ ...[{ ...firstItem, [valueCompareField]: firstItem[compareField] }]
+ ]) : onChange(firstItem)
+ } else {
+ onSelect(firstItem)
+ }
+ } */
+ }
+ }
+ })
+
+ pagination && this.setState({
+ pagination
+ })
+ }
+
+ componentWillUpdate(nextProps, nextState) {
+ const {
+ concurrentMultiSelect,
+ input: { value },
+ items,
+ compareField,
+ displayName,
+ } = nextProps
+ const { search, oldItems, oldValue } = nextState
+
+ if (concurrentMultiSelect && !search && oldItems.length < value.length) {
+ this.setState({
+ selectedItems: value,
+ oldItems: value
+ })
+ }
+
+ if (typeof items !== 'undefined' && !search) {
+
+ let newValue = ''
+
+ for (const key in items) {
+ if (key !== 'undefined' && items[key]['fullItem'][compareField] === value) {
+ newValue = items[key]['fullItem'][displayName]
+ }
+ }
+
+ if (value && (oldValue !== value)) {
+ this.setState({
+ oldValue: compareField ? value : value[displayName],
+ values: compareField ? newValue : value[displayName],
+ })
+ }
+
+ }
+
+ }
+
+ onGetItems = ({
+ fresh = false,
+ onResult,
+ q = '',
+ } = {}) => {
+
+ const { getItems } = this.props
+
+ const {
+ refreshing,
+ pagination
+ } = this.state;
+
+ if (refreshing) {
+ return;
+ }
+
+ const paginationParams = fresh ? { ...pagination, page: 1 } : pagination
+
+ if (!fresh && paginationParams.lastPage < paginationParams.page) {
+ return
+ }
+
+ this.setState({
+ refreshing: true,
+ })
+
+ getItems && getItems({
+ fresh,
+ pagination: paginationParams,
+ params: { search: q },
+ q,
+ onMeta: ({ last_page, current_page }) => {
+ this.setState({
+ pagination: {
+ ...paginationParams,
+
+ lastPage: last_page,
+ page: current_page + 1
+ }
+ })
+ },
+ onResult: () => {
+ this.setState({
+ refreshing: false,
+ })
+ onResult && onResult();
+ },
+ });
+
+ }
+
+ onToggle = () => {
+ const { meta, isEditable = true } = this.props
+ const { visible, defaultItem } = this.state
+
+
+ if (isEditable) {
+ if (visible)
+ this.setState({ searchItems: defaultItem })
+
+ this.setState((prevState) => {
+ return { visible: !prevState.visible }
+ });
+
+ meta.dispatch(change(meta.form, 'search', ''));
+ }
+ }
+
+ onItemSelect = (item) => {
+ const {
+ concurrentMultiSelect
+ } = this.props
+ concurrentMultiSelect ? this.toggleItem(item) : this.getAlert(item)
+
+ }
+
+ toggleItem = (item) => {
+ const {
+ compareField,
+ valueCompareField,
+ } = this.props
+
+ const { selectedItems } = this.state
+
+ const newItem = [{ ...item, [valueCompareField]: item[compareField] }]
+
+ if (selectedItems) {
+ let hasSameItem = selectedItems.filter(val =>
+ JSON.parse(val[valueCompareField]) === JSON.parse(item[compareField])
+ )
+
+ if (hasSameItem.length > 0) {
+ const removedItems = selectedItems.filter(val =>
+ JSON.parse(val[valueCompareField]) !== JSON.parse(item[compareField])
+ )
+
+ this.setState({ selectedItems: removedItems })
+ } else {
+ this.setState({
+ selectedItems: [...selectedItems, ...newItem],
+ })
+ }
+ } else {
+ this.setState({ selectedItems: newItem })
+ }
+ }
+
+ getAlert = (item) => {
+ const {
+ displayName,
+ input: { onChange, value },
+ isMultiSelect,
+ onlyPlaceholder,
+ onSelect,
+ compareField,
+ valueCompareField,
+ isCompareField = true
+ } = this.props
+
+ if (!isMultiSelect && value) {
+ const hasCompare = compareField ? value === item[compareField] :
+ JSON.parse(value.id) === JSON.parse(item.id)
+
+ if (hasCompare) {
+ // alert(`The ${item[displayName]} already added`)
+ this.onToggle()
+ return
+ }
+ }
+
+ if (isMultiSelect && value) {
+ let hasSameItem = value.filter(val => JSON.parse(val[valueCompareField]) === JSON.parse(item[compareField]))
+
+ if (hasSameItem.length > 0) {
+ // alert(`The ${item[displayName]} already added`)
+ this.onToggle()
+ return
+ }
+ }
+
+ !onlyPlaceholder && this.setState({
+ values: item[displayName]
+ })
+
+ if (!onSelect) {
+ isMultiSelect ?
+ onChange([
+ ...value,
+ ...[{ ...item, [valueCompareField]: item[compareField] }]
+ ]) : onChange(item)
+ } else {
+ onSelect(item)
+ }
+
+ !onlyPlaceholder && this.setState({
+ oldValue: item[compareField]
+ })
+
+ this.onToggle()
+ }
+
+ onSearch = (search) => {
+ this.setState({ search })
+ const { apiSearch, isInternalSearch } = this.props;
+
+ apiSearch && !isInternalSearch ? this.onGetItems({ fresh: true, q: search }) : this.internalSearch(search)
+ }
+
+ internalSearch = (search) => {
+
+ const { items, searchFields, onSearch, isInternalSearch } = this.props;
+ let newData = [];
+ const { defaultItem } = this.state
+
+ let searchItems = isInternalSearch ? items : defaultItem
+
+ if (typeof searchItems !== 'undefined' && searchItems.length != 0) {
+ newData = searchItems.filter((item) => {
+ let filterData = false
+
+ searchFields.filter((field) => {
+ let itemField = item.fullItem[field]
+
+ if (typeof itemField === 'number') {
+ itemField = itemField.toString()
+ }
+
+ if (itemField !== null && typeof itemField !== 'undefined') {
+ itemField = itemField.toLowerCase()
+
+ let searchData = search.toString().toLowerCase()
+
+ if (itemField.indexOf(searchData) > -1) {
+ filterData = true
+ }
+ }
+ })
+ return filterData
+ });
+ }
+
+ this.setState({ searchItems: newData })
+ }
+
+ onSubmit = () => {
+ const { input: { onChange, value } } = this.props
+
+ const { selectedItems } = this.state
+
+ onChange(selectedItems)
+
+ this.setState({
+ oldItems: selectedItems
+ })
+
+ this.onToggle()
+ }
+
+ onRightIconPress = () => {
+ const { rightIconPress } = this.props
+ this.onToggle()
+ rightIconPress && rightIconPress()
+ }
+
+ BOTTOM_ACTION = () => {
+ const { language } = this.props
+
+ return (
+
+
+
+
+
+ )
+ }
+
+ getEmptyTitle = () => {
+ const { language, emptyContentProps: { contentType } } = this.props
+ const { search } = this.state
+
+ let emptyTitle = ''
+
+ if (contentType) {
+ emptyTitle = Lng.t(`${contentType}.empty.title`, { locale: language })
+ }
+
+ let noSearchResult = Lng.t("search.noSearchResult", { locale: language })
+
+ return search ? `${noSearchResult} "${search}"` : emptyTitle
+ }
+
+ render() {
+ const {
+ containerStyle,
+ items,
+ loading,
+ label,
+ icon,
+ placeholder,
+ meta,
+ headerProps,
+ hasPagination,
+ fakeInputProps,
+ listViewProps,
+ valueCompareField,
+ compareField,
+ concurrentMultiSelect,
+ emptyContentProps,
+ apiSearch,
+ searchInputProps,
+ input: { value },
+ isRequired,
+ isInternalSearch
+ } = this.props;
+
+ const {
+ refreshing,
+ visible,
+ search,
+ pagination: { lastPage, page },
+ values,
+ selectedItems,
+ searchItems,
+ } = this.state
+
+ const canLoadMore = (lastPage >= page)
+
+ let paginationContent = {}
+ let multiSelectProps = {}
+ let bottomActionProps = {}
+
+ if (concurrentMultiSelect) {
+ multiSelectProps = {
+ hasCheckbox: true,
+ compareField,
+ valueCompareField,
+ checkedItems: selectedItems,
+ }
+ bottomActionProps = {
+ bottomAction: this.BOTTOM_ACTION()
+ }
+ }
+
+ if (hasPagination) {
+ paginationContent = {
+ canLoadMore,
+ getFreshItems: (onHide) => {
+ this.onGetItems({
+ fresh: true,
+ onResult: onHide,
+ q: search,
+ })
+ },
+ getItems: () => {
+ this.onGetItems({
+ q: search,
+ });
+ },
+ }
+ }
+
+ let internalSearchItem = (isInternalSearch && !search) ? items : searchItems
+
+ return (
+
+
+
+
+ this.onToggle(),
+ titleStyle: headerTitle({}),
+ placement: "center",
+ rightIcon: "plus",
+ hasCircle: false,
+ noBorder: false,
+ transparent: false,
+ rightIconPress: () => this.onRightIconPress(),
+ ...headerProps
+ }}
+ searchInputProps={searchInputProps && searchInputProps}
+ onSearch={this.onSearch}
+ bottomDivider
+ {...paginationContent}
+ {...bottomActionProps}
+ listViewProps={{
+ items: apiSearch ? items : internalSearchItem,
+ onPress: this.onItemSelect,
+ refreshing: refreshing,
+ loading: loading,
+ isEmpty: typeof items == 'undefined' || (apiSearch ?
+ items.length <= 0 : internalSearchItem.length <= 0),
+ bottomDivider: true,
+ emptyContentProps: {
+ title: this.getEmptyTitle(),
+ ...emptyContentProps
+ },
+ ...listViewProps,
+ ...multiSelectProps,
+ ...paginationContent
+ }}
+ />
+
+ );
+ }
+}
+
+const mapStateToProps = ({ global }) => ({
+ language: global.language,
+});
+
+const mapDispatchToProps = {};
+
+export const SelectField = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(SelectFieldComponent);
diff --git a/src/components/SelectField/styles.js b/src/components/SelectField/styles.js
new file mode 100644
index 00000000..422c3f94
--- /dev/null
+++ b/src/components/SelectField/styles.js
@@ -0,0 +1,18 @@
+import { StyleSheet } from 'react-native';
+
+export default StyleSheet.create({
+ container: {
+ flex: 1,
+ // marginTop: 10,
+ },
+ submitButton: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ },
+ handleBtn: {
+ marginHorizontal: 9,
+ },
+ btnWhite: {
+ paddingVertical: 6,
+ },
+});
diff --git a/src/components/SelectField/type.js b/src/components/SelectField/type.js
new file mode 100644
index 00000000..95c35131
--- /dev/null
+++ b/src/components/SelectField/type.js
@@ -0,0 +1,48 @@
+// @flow
+
+export type IProps = {
+
+ loading: Boolean,
+ apiSearch: Boolean,
+ concurrentMultiSelect: Boolean,
+ isRequired: Boolean,
+ pagination: Boolean,
+ isMultiSelect: Boolean,
+ isInternalSearch: Boolean,
+ hasPagination: Boolean,
+
+ onChangeCallback: Function,
+ getItem: Function,
+ onSearch: Function,
+ getItems: Function,
+ rightIconPress: Function,
+ onSelect: Function,
+
+ label: String,
+ icon: String,
+ placeholder: String,
+ rightIcon: String,
+ leftIcon: String,
+ color: String,
+ value: String,
+ displayName: String,
+ emptyContentImage: String,
+ compareField: String,
+ valueCompareField: String,
+ language: String,
+
+ containerStyle: Object,
+ meta: Object,
+ headerProps: Object,
+ input: Object,
+ fakeInputProps: Object,
+ onlyPlaceholder: Object,
+ listViewProps: Object,
+ searchInputProps: Object,
+ input: Object,
+ emptyContentProps: Object,
+
+ items: Array,
+ searchFields: Array
+
+};
diff --git a/src/components/SelectPickerField/index.js b/src/components/SelectPickerField/index.js
new file mode 100644
index 00000000..55da3601
--- /dev/null
+++ b/src/components/SelectPickerField/index.js
@@ -0,0 +1,225 @@
+// @flow
+
+import React, { Component } from 'react';
+import { View, Text } from 'react-native';
+import RNPickerSelect from 'react-native-picker-select';
+
+import styles from './styles';
+import { FakeInput } from '../FakeInput';
+import FakeInputStyle from '../FakeInput/styles';
+
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { colors } from '../../styles/colors';
+import { isIosPlatform } from '../../api/helper';
+
+type IProps = {
+ hint: string,
+ lightTheme: boolean,
+ disabled: boolean,
+ input: {
+ onChange: Function,
+ value: string,
+ },
+ meta: Object,
+ fakeInputContainerStyle: Object,
+ defaultPickerOptions: Object,
+ items: Array,
+ ref: Function,
+ onChangeCallback: Function,
+ refLinkFn: Function,
+ onDonePress: Function,
+ doneText: string,
+ fieldIcon: string,
+ containerStyle: Object,
+ fakeInputValueStyle: Object,
+ label: String,
+ isRequired: Boolean,
+ isFakeInput: Boolean,
+};
+
+export class SelectPickerField extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ icon: 'angle-down',
+ initialValue: '',
+ };
+ }
+
+ componentDidMount() {
+ const { input: { value, onChange }, onChangeCallback } = this.props
+
+ onChange(value)
+
+ onChangeCallback && onChangeCallback(value);
+ }
+
+ toggleIcon = () => {
+ const { icon } = this.state
+ this.setState({
+ icon: icon === 'angle-down' ? 'angle-up' : 'angle-down',
+ })
+ }
+
+ onChange = (v) => {
+ const {
+ onChangeCallback,
+ input: { onChange }
+ } = this.props
+
+ onChange(v)
+
+ this.setState({ initialValue: v })
+ onChangeCallback && onChangeCallback(v);
+ }
+
+ onDonePress = (selectRef) => {
+ const { onDonePress, isFakeInput } = this.props
+
+ onDonePress && onDonePress()
+ }
+
+ render() {
+ const {
+ input: { value },
+ meta,
+ hint,
+ items,
+ ref,
+ disabled,
+ isRequired,
+ refLinkFn,
+ doneText,
+ fieldIcon,
+ defaultPickerOptions,
+ fakeInputContainerStyle,
+ containerStyle,
+ label,
+ isFakeInput,
+ fakeInputValueStyle,
+ } = this.props;
+
+ const { icon } = this.state
+ let selectRef = null
+ let selected = items && items.find((item) => item.value === value);
+ let selectedLabel = selected && (selected.displayLabel || selected.label);
+ let selectedValue = selected && selected.value;
+
+ let placeHolder = {
+ ...{ color: colors.darkGray },
+ ...defaultPickerOptions,
+ }
+
+ const pickerField = (
+ ({ ...item, color: colors.secondary }))}
+ onValueChange={(v) => {
+ this.onChange(v);
+ }}
+ style={{
+ inputIOS: {
+ ...styles.inputIOS,
+ ...(disabled ? styles.disabledSelectedValue : {}),
+ ...(fakeInputContainerStyle && fakeInputContainerStyle),
+ ...(!isFakeInput && { paddingLeft: 41 })
+ },
+ inputIOSContainer: {
+ ...(isFakeInput && { display: 'none' })
+ },
+ iconContainer: {
+ top: 13,
+ right: 11,
+ },
+ placeholder: {
+ fontSize: 15,
+ },
+ }}
+ onOpen={() => this.toggleIcon()}
+ onClose={() => this.toggleIcon()}
+ value={typeof selectedValue !== 'undefined' && selectedValue}
+ placeholderTextColor={colors.darkGray}
+ ref={(dropdownRef = {}) => {
+ refLinkFn &&
+ refLinkFn({
+ ...dropdownRef,
+ focus: () => dropdownRef && dropdownRef.togglePicker(),
+ })
+ selectRef = dropdownRef && dropdownRef
+ }}
+ Icon={() => (
+
+
+
+ )}
+ modalProps={{
+ animationType: 'slide'
+ }}
+ disabled={disabled}
+ onDonePress={() => this.onDonePress(selectRef)}
+ doneText={doneText}
+ >
+ {!isIosPlatform() && (
+
+
+ {!selectedLabel ?
+ (defaultPickerOptions && (defaultPickerOptions.displayLabel || defaultPickerOptions.label)) : selectedLabel
+ }
+
+
+
+ )}
+
+ );
+
+ const isFakeDisplay = isIosPlatform() && isFakeInput
+
+ return (
+
+ isFakeDisplay && selectRef.togglePicker()}
+ containerStyle={containerStyle}
+ />
+ {isFakeDisplay && pickerField}
+
+ );
+ }
+}
diff --git a/src/components/SelectPickerField/styles.js b/src/components/SelectPickerField/styles.js
new file mode 100644
index 00000000..71f8ce56
--- /dev/null
+++ b/src/components/SelectPickerField/styles.js
@@ -0,0 +1,41 @@
+import { StyleSheet, Platform } from 'react-native';
+
+import styles from '../FakeInput/styles';
+import { colors } from '../../styles/colors';
+
+export default StyleSheet.create({
+ fakeInput: {
+ ...styles.fakeInput,
+ paddingRight: 10,
+ height: 47,
+ ...Platform.select({
+ ios: {
+ height: 42,
+ },
+ }),
+ },
+ androidPicker: {
+ height: 40,
+ marginTop: -10,
+ color: colors.secondary,
+ },
+ inputIOS: {
+ ...styles.fakeInput,
+ ...styles.textValue,
+ paddingRight: 20,
+ height: 42,
+ },
+ disabledSelectedValue: {
+ ...styles.disabledSelectedValue,
+ },
+ rightIcon: {
+ paddingLeft: 5,
+ alignItems: "center",
+ justifyContent: "center",
+ ...Platform.select({
+ android: {
+ paddingTop: 3,
+ },
+ }),
+ }
+});
diff --git a/src/components/SlideModal/index.js b/src/components/SlideModal/index.js
new file mode 100644
index 00000000..298abb1e
--- /dev/null
+++ b/src/components/SlideModal/index.js
@@ -0,0 +1,98 @@
+import React, { Component } from 'react';
+
+import {
+ View,
+ Modal,
+} from 'react-native';
+import styles from './styles';
+import { ListView } from '../ListView';
+import { MainLayout, DefaultLayout } from '../Layouts';
+
+type IProps = {
+ visible: Boolean,
+ onToggle: Function,
+ headerProps: Object,
+ onSearch: Function,
+ bottomDivider: Boolean,
+ hasSearchField: Boolean,
+ listViewProps: Object,
+ defaultLayout: Boolean,
+ children: Object,
+ bottomAction: Object,
+ searchInputProps: Object,
+};
+
+export class SlideModal extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ };
+ }
+
+ render() {
+
+ const {
+ visible,
+ onToggle,
+ headerProps,
+ onSearch,
+ bottomDivider = false,
+ listViewProps,
+ hasListView,
+ imageListView,
+ defaultLayout,
+ children,
+ hasSearchField,
+ bottomAction,
+ searchInputProps
+ } = this.props
+
+ return (
+
+
+ {!defaultLayout && (
+
+
+
+
+
+ )}
+
+ {defaultLayout && (
+
+ {children ? (
+
+ {children}
+
+ ) : (
+
+
+
+ )}
+
+
+ )}
+
+
+ );
+ }
+}
+
diff --git a/src/components/SlideModal/styles.js b/src/components/SlideModal/styles.js
new file mode 100644
index 00000000..2fb02c82
--- /dev/null
+++ b/src/components/SlideModal/styles.js
@@ -0,0 +1,25 @@
+import { Platform, StyleSheet } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+import { isIPhoneX } from '../../api/helper';
+
+export default StyleSheet.create({
+ listViewContainer: {
+ flex: 1,
+ paddingBottom: isIPhoneX() ? 30 : 0,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ modalContainer: {
+ flex: 1,
+ ...Platform.select({
+ android: {
+ marginTop: -20,
+ margin: 0,
+ padding: 0
+ },
+ }),
+ },
+});
diff --git a/src/components/Tabs/index.js b/src/components/Tabs/index.js
new file mode 100644
index 00000000..0755f2d0
--- /dev/null
+++ b/src/components/Tabs/index.js
@@ -0,0 +1,64 @@
+import React, { Component } from 'react';
+import { View, TouchableOpacity, Text } from 'react-native';
+
+import { styles } from './styles';
+import { getConditionStyles } from '../../api/helper';
+
+type IProps = {
+ activeTab: boolean,
+ tabs: Array,
+ setActiveTab: Function,
+ style: Object,
+};
+
+export class Tabs extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {};
+ }
+
+ componentWillMount() {
+ const { activeTab, setActiveTab, tabs } = this.props;
+ !activeTab && setActiveTab(tabs[0].Title || tabs[0].id);
+ }
+
+ render() {
+ const { activeTab, tabs, setActiveTab, style } = this.props;
+
+ let { render } = tabs.find(({ id, Title }) => [id, Title].includes(activeTab));
+
+ return (
+
+
+ {tabs.map(({ id, Title, tabName }) => (
+ setActiveTab(Title)}
+ >
+ {typeof tabName === 'string' ? (
+
+ {tabName && tabName}
+
+ ) : (
+ tabName && tabName({ active: [tabName, id].includes(activeTab) })
+ )}
+
+ ))}
+
+
+ {render}
+
+ );
+ }
+}
+
diff --git a/src/components/Tabs/styles.js b/src/components/Tabs/styles.js
new file mode 100644
index 00000000..8495024f
--- /dev/null
+++ b/src/components/Tabs/styles.js
@@ -0,0 +1,37 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+
+export const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ tabs: {
+ width: '100%',
+ height: 38,
+ display: 'flex',
+ flexDirection: 'row',
+ },
+ tab: {
+ flex: 1,
+ borderBottomWidth: 2,
+ borderColor: colors.darkGray,
+ fontFamily: fonts.poppinsSemiBold,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ selected_tab: {
+ borderBottomWidth: 2,
+ borderBottomColor: colors.primary,
+ },
+ TabTitle: {
+ color: colors.darkGray,
+ fontFamily: fonts.poppinsSemiBold,
+ },
+ selectedTabTitle: {
+ color: colors.primary,
+ },
+});
diff --git a/src/components/Toast/index.js b/src/components/Toast/index.js
new file mode 100644
index 00000000..9b9e786b
--- /dev/null
+++ b/src/components/Toast/index.js
@@ -0,0 +1,79 @@
+import React, { Component } from 'react'
+import { Text, Animated } from 'react-native';
+import { styles } from './styles';
+
+type IProps = {
+ visible: Boolean,
+ message: String,
+ containerStyle: Object
+};
+
+
+export class Toast extends Component {
+ constructor() {
+ super();
+ this.animateOpacityValue = new Animated.Value(0);
+ }
+
+ componentWillUnmount() {
+ this.timerID && clearTimeout(this.timerID);
+ }
+
+ ShowToastFunction(duration = 1000) {
+ Animated.timing
+ (
+ this.animateOpacityValue,
+ {
+ toValue: 0.8,
+ duration: 200
+ }
+ ).start(this.HideToastFunction(duration))
+
+ }
+
+ HideToastFunction = (duration) => {
+ this.timerID = setTimeout(() => {
+ Animated.timing
+ (
+ this.animateOpacityValue,
+ {
+ toValue: 0,
+ duration: 200
+ }
+ ).start(() => {
+ clearTimeout(this.timerID);
+ })
+ }, duration);
+ }
+
+ render() {
+ const { visible = false, message, containerStyle } = this.props
+ visible && this.ShowToastFunction()
+
+ if (visible) {
+ return (
+
+
+
+ {message}
+
+
+
+ );
+ }
+ else {
+ return null;
+ }
+ }
+}
+
+export default Toast
\ No newline at end of file
diff --git a/src/components/Toast/styles.js b/src/components/Toast/styles.js
new file mode 100644
index 00000000..26c750dd
--- /dev/null
+++ b/src/components/Toast/styles.js
@@ -0,0 +1,33 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+import { isIPhoneX } from '../../api/helper';
+
+export const styles = StyleSheet.create({
+
+ animatedToastView:
+ {
+ marginHorizontal: 22,
+ paddingHorizontal: 25,
+ paddingVertical: 11,
+ borderRadius: 25,
+ zIndex: 9999,
+
+ position: 'absolute',
+ bottom: isIPhoneX() ? 110 : 81,
+ left: 0,
+ right: 0,
+
+ backgroundColor: colors.veryDarkGray,
+ justifyContent: 'center',
+ },
+
+ title:
+ {
+ fontSize: isIPhoneX() ? 15 : 13,
+ fontFamily: fonts.poppins,
+ alignSelf: 'stretch',
+ textAlign: 'center',
+ color: colors.white
+ }
+});
diff --git a/src/components/ToggleSwitch/index.js b/src/components/ToggleSwitch/index.js
new file mode 100644
index 00000000..4b623bcb
--- /dev/null
+++ b/src/components/ToggleSwitch/index.js
@@ -0,0 +1,107 @@
+import React, { Component } from 'react';
+import { Switch, View, Text } from 'react-native';
+import { colors } from '../../styles/colors';
+import { styles } from './styles';
+
+type IProps = {
+ input: Object,
+ disabled: Boolean,
+ meta: Object,
+ switchStyle: Object,
+ containerStyle: Object,
+ hint: string,
+ description: String,
+ switchType: String,
+};
+export class ToggleSwitch extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ loading: false,
+ status: false,
+ };
+ }
+
+ componentDidMount() {
+ const { status, input: { value } } = this.props
+ if (status)
+ this.setState({ status })
+ if (!status && value) {
+ this.setState({ status: value ? true : false })
+ }
+ }
+
+ onToggle = () => {
+ const { onChangeCallback, input: { onChange } } = this.props
+ const { status } = this.state
+
+ this.setState((prevState) => {
+ return { status: !prevState.status }
+ })
+
+ onChange(!status)
+
+ onChangeCallback && onChangeCallback(!status)
+ }
+
+ render() {
+
+ const { loading, status } = this.state;
+
+ const {
+ switchType,
+ hint,
+ description,
+ containerStyle,
+ mainContainerStyle,
+ input: { value },
+ switchStyle
+ } = this.props;
+
+ return (
+
+
+ {
+ hint && (
+
+ {hint}
+
+ )
+ }
+ this.onToggle()}
+ value={status}
+
+ style={[
+ styles.switchStyle,
+ switchStyle && switchStyle
+ ]}
+ />
+
+ {
+ description && (
+
+
+ {description}
+
+
+ )
+ }
+
+ );
+ }
+}
+
+export default ToggleSwitch;
diff --git a/src/components/ToggleSwitch/styles.js b/src/components/ToggleSwitch/styles.js
new file mode 100644
index 00000000..c5a92ad2
--- /dev/null
+++ b/src/components/ToggleSwitch/styles.js
@@ -0,0 +1,39 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../styles/colors';
+import { fonts } from '../../styles/fonts';
+import { isIosPlatform } from '../../api/helper';
+
+export const styles = StyleSheet.create({
+ mainContainer: {
+ },
+ container: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ flexWrap: 'nowrap',
+ flexDirection: 'row',
+ marginVertical: 15
+ },
+ hintStyle: {
+ color: colors.secondary,
+ fontFamily: fonts.poppins,
+ fontSize: 16,
+ marginTop: 3,
+ },
+ switchStyle: {
+ transform: isIosPlatform() ? [{ scaleX: 0.8 }, { scaleY: 0.8 }] : [{ scaleX: 1.2 }, { scaleY: 1.2 }],
+ },
+ switchContainer: {
+ height: 20,
+ },
+ descriptionContainer: {
+ flex: 1,
+ paddingRight: 8,
+ marginTop: -5,
+ },
+ description: {
+ textAlign: "justify",
+ color: colors.darkGray,
+ fontFamily: fonts.poppinsLight,
+ fontSize: 14,
+ }
+});
diff --git a/src/components/UpdateAppVersion/index.js b/src/components/UpdateAppVersion/index.js
new file mode 100644
index 00000000..4d1e945c
--- /dev/null
+++ b/src/components/UpdateAppVersion/index.js
@@ -0,0 +1,107 @@
+import React, { Component } from 'react'
+import { View, Platform, Linking } from 'react-native'
+import { connect } from 'react-redux';
+import styles from './styles';
+import { AssetImage } from '../AssetImage';
+import { CtGradientButton } from '../Button';
+import { Text } from 'react-native-elements';
+import { IMAGES } from '../../config';
+import Lng from '../../api/lang/i18n';
+import { goBack, MOUNT, UNMOUNT } from '../../navigation/actions';
+import { ROUTES } from '../../navigation/routes';
+
+export class UpdateAppVersion extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ loading: false
+ };
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation, ROUTES.UPDATE_APP_VERSION)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ onUpdateApp = () => {
+
+ this.setState({ loading: true })
+
+ setTimeout(() => {
+ this.setState({ loading: false })
+ }, 1000);
+
+ Platform.OS === 'android' ?
+ Linking.openURL('https://craterapp.com/') :
+ Linking.openURL('https://craterapp.com/')
+ }
+
+ render() {
+
+ const { language } = this.props;
+ const { loading } = this.state
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {Lng.t("updateApp.title", { locale: language })}
+
+
+
+ {Lng.t("updateApp.description", { locale: language })}
+
+
+
+
+
+ this.onUpdateApp()}
+ btnTitle={Lng.t("button.updateCapital", { locale: language })}
+ loading={loading}
+ />
+
+
+
+
+ )
+ }
+}
+
+const mapStateToProps = ({ global }) => ({
+ language: global.language,
+});
+
+const mapDispatchToProps = {
+
+};
+
+
+// connect
+const UpdateAppVersionContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(UpdateAppVersion);
+
+UpdateAppVersionContainer.navigationOptions = () => ({
+ header: null,
+});
+
+
+export default UpdateAppVersionContainer;
diff --git a/src/components/UpdateAppVersion/styles.js b/src/components/UpdateAppVersion/styles.js
new file mode 100644
index 00000000..1fd3f441
--- /dev/null
+++ b/src/components/UpdateAppVersion/styles.js
@@ -0,0 +1,48 @@
+import { StyleSheet, Dimensions } from 'react-native';
+import { colors } from '../../styles/colors';
+
+
+const { width, height } = Dimensions.get('window');
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'flex-start',
+ display: 'flex',
+ backgroundColor: colors.veryLightGray
+ },
+ main: {
+ flex: 1,
+ flexDirection: 'column',
+ paddingHorizontal: 25,
+ justifyContent: 'center',
+ },
+ logoContainer: {
+ paddingBottom: 30,
+ alignItems: 'center',
+ marginTop: -70,
+ },
+ imgLogo: {
+ width: width - 150,
+ height: 120,
+ resizeMode: 'contain',
+ },
+ bodyContainer: {
+ textAlign: "center",
+ alignItems: "center",
+ },
+ title: {
+ paddingBottom: 10,
+ color: colors.primary,
+ },
+ subTitle: {
+ paddingTop: 5,
+ color: colors.primaryLight,
+ },
+ description: {
+ paddingHorizontal: 10,
+ paddingTop: 18,
+ color: colors.primaryLight,
+ textAlign: "center",
+ },
+});
diff --git a/src/components/index.js b/src/components/index.js
new file mode 100644
index 00000000..b9014443
--- /dev/null
+++ b/src/components/index.js
@@ -0,0 +1,30 @@
+// Layouts
+export * from './Layouts'
+
+// Components
+export * from './Divider'
+export * from './Button'
+export * from './AssetImage'
+export * from './Header'
+export * from './InputField'
+export * from './Tabs'
+export * from './Content'
+export * from './Empty'
+export * from './ListView'
+export * from './Loading'
+export * from './FakeInput'
+export * from './DatePickerField'
+export * from './GradientBackground'
+export * from './SelectPickerField'
+export * from './ToggleSwitch';
+export * from './RadioButtonGroup';
+export * from './SlideModal'
+export * from './FilePicker'
+export * from './SelectField'
+export * from './Dropdown'
+export * from './Filter'
+export * from './AppLoader'
+export * from './LostConnection'
+export * from './UpdateAppVersion'
+export * from './CurrencyFormat'
+export * from './Toast'
diff --git a/src/config/images.js b/src/config/images.js
new file mode 100644
index 00000000..41155cfa
--- /dev/null
+++ b/src/config/images.js
@@ -0,0 +1,34 @@
+import LogoDark from '../assets/crater-logo.png';
+import GoogleIcon from '../assets/google.png';
+import EmptyInvoices from '../assets/empty-invoices-icon.png';
+import EmptyCustomers from '../assets/empty-customers-icon.png';
+import EmptyEstimates from '../assets/empty-estimates-icon.png';
+import EmptyExpenses from '../assets/empty-expenses-icon.png';
+import EmptyItems from '../assets/empty-items-icon.png';
+import EmptyPayments from '../assets/empty-payments-icon.png';
+import LostConnection from '../assets/lost-connection.png';
+import OpenEnvelop from '../assets/envelop.png';
+
+export const IMAGES = {
+ LOGO_DARK: LogoDark,
+ GOOGLE_ICON: GoogleIcon,
+ EMPTY_INVOICES: EmptyInvoices,
+ EMPTY_CUSTOMERS: EmptyCustomers,
+ EMPTY_ESTIMATES: EmptyEstimates,
+ EMPTY_EXPENSES: EmptyExpenses,
+ EMPTY_ITEMS: EmptyItems,
+ EMPTY_PAYMENTS: EmptyPayments,
+ LOST_CONNECTION: LostConnection,
+ OPEN_ENVELOP: OpenEnvelop
+};
+
+export const EMPTY_IMAGES = {
+ INVOICES: EmptyInvoices,
+ CUSTOMERS: EmptyCustomers,
+ ESTIMATES: EmptyEstimates,
+ EXPENSES: EmptyExpenses,
+ ITEMS: EmptyItems,
+ PAYMENTS: EmptyPayments,
+};
+
+export const ICONS = {};
diff --git a/src/config/index.js b/src/config/index.js
new file mode 100644
index 00000000..7009050f
--- /dev/null
+++ b/src/config/index.js
@@ -0,0 +1,3 @@
+export { default as env } from '../../config';
+
+export * from './images';
diff --git a/src/features/authentication/actions/index.js b/src/features/authentication/actions/index.js
new file mode 100644
index 00000000..14689947
--- /dev/null
+++ b/src/features/authentication/actions/index.js
@@ -0,0 +1,98 @@
+// @flow
+import { ILoginPayload } from './types';
+import {
+ LOGIN,
+ LOGIN_SUCCESS,
+ SOCIAL_LOGIN,
+ SAVE_ID_TOKEN,
+ AUTH_TRIGGER_SPINNER,
+ SEND_FORGOT_PASSWORD_MAIL,
+ GET_BOOTSTRAP,
+ SET_BOOTSTRAP,
+ RESET_ID_TOKEN,
+ CHECK_ENDPOINT_API,
+} from '../constants';
+import {
+ SET_GLOBAL_BOOTSTRAP,
+ GLOBAL_TRIGGER_SPINNER,
+ SAVE_ENDPOINT_API,
+ GET_APP_VERSION,
+ SET_APP_VERSION
+} from '../../../api/consts';
+
+export const login = (payload: ILoginPayload) => ({
+ type: LOGIN,
+ payload,
+});
+
+export const socialLogin = (payload) => ({
+ type: SOCIAL_LOGIN,
+ payload,
+});
+
+export const loginSuccess = ({ idToken }) => ({
+ type: LOGIN_SUCCESS,
+ payload: { idToken },
+});
+
+export const saveIdToken = (payload = {}) => ({
+ type: SAVE_ID_TOKEN,
+ payload,
+});
+
+export const resetIdToken = () => ({
+ type: RESET_ID_TOKEN,
+});
+
+export const authTriggerSpinner = (payload) => ({
+ type: AUTH_TRIGGER_SPINNER,
+ payload,
+});
+
+export const sendForgotPasswordMail = (payload) => ({
+ type: SEND_FORGOT_PASSWORD_MAIL,
+ payload,
+});
+
+export const getBootstrap = (payload) => ({
+ type: GET_BOOTSTRAP,
+ payload,
+});
+
+export const getAppVersion = (payload) => ({
+ type: GET_APP_VERSION,
+ payload,
+});
+
+export const setBootstrap = (payload) => ({
+ type: SET_BOOTSTRAP,
+ payload,
+});
+
+export const setAppVersion = (payload) => ({
+ type: SET_APP_VERSION,
+ payload,
+});
+
+// Global settings
+export const setGlobalBootstrap = (payload = {}) => ({
+ type: SET_GLOBAL_BOOTSTRAP,
+ payload,
+});
+
+export const globalTriggerSpinner = (payload = {}) => ({
+ type: GLOBAL_TRIGGER_SPINNER,
+ payload,
+});
+
+export const saveEndpointApi = (payload) => ({
+ type: SAVE_ENDPOINT_API,
+ payload,
+});
+
+export const checkEndpointApi = (payload = {}) => ({
+ type: CHECK_ENDPOINT_API,
+ payload,
+});
+
+
diff --git a/src/features/authentication/actions/types.js b/src/features/authentication/actions/types.js
new file mode 100644
index 00000000..a7b22976
--- /dev/null
+++ b/src/features/authentication/actions/types.js
@@ -0,0 +1,6 @@
+// @flow
+
+export type ILoginPayload = {
+ email: string,
+ password: string,
+};
diff --git a/src/features/authentication/components/Endpoint/index.js b/src/features/authentication/components/Endpoint/index.js
new file mode 100644
index 00000000..50b6e5bb
--- /dev/null
+++ b/src/features/authentication/components/Endpoint/index.js
@@ -0,0 +1,170 @@
+// @flow
+
+import React, { Component } from 'react';
+import {
+ View,
+ KeyboardAvoidingView,
+ Text,
+ StatusBar,
+ ScrollView
+} from 'react-native';
+import styles from './styles';
+import { Field } from 'redux-form';
+import { InputField, CtButton, AssetImage, CtGradientButton, CtHeader } from '../../../../components';
+import Lng from '../../../../api/lang/i18n';
+import { ROUTES } from '../../../../navigation/routes';
+import { IMAGES } from '../../../../config';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { alertMe } from '../../../../api/global';
+
+type IProps = {
+ label: String,
+ icon: String,
+ placeholder: String,
+ containerStyle: Object,
+ rightIcon: String,
+ leftIcon: String,
+ color: String,
+ value: String,
+ items: Object,
+ rightIcon: String,
+ loading: Boolean,
+ checkEndpointApi: Function
+};
+
+export class Endpoint extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ isFocus: false
+ };
+ }
+
+
+ componentDidMount() {
+
+ const { navigation, skipEndpoint } = this.props
+
+ skipEndpoint && goBack(MOUNT, navigation, ROUTES.SETTING_LIST)
+ }
+
+ componentWillUnmount() {
+ const { skipEndpoint } = this.props
+ skipEndpoint && goBack(UNMOUNT)
+ }
+
+
+ onSetEndpointApi = ({ endpointURL }) => {
+
+ this.setState({ isFocus: false })
+
+ const { checkEndpointApi, navigation, language } = this.props
+ let URL = endpointURL
+
+ checkEndpointApi({
+ endpointURL: !(URL.charAt(URL.length - 1) === '/') ? URL
+ : URL.slice(0, -1),
+ onResult: (val) => {
+ !val ? alertMe({ title: Lng.t("endpoint.alertInvalidUrl", { locale: language }) }) :
+ navigation.navigate(ROUTES.LOGIN)
+
+ }
+ })
+ }
+
+ onBack = () => {
+ this.props.navigation.navigate(ROUTES.SETTING_LIST)
+ }
+
+ toggleFocus = () => {
+ this.setState((prevState) => {
+ return { isFocus: !prevState.isFocus }
+ });
+ }
+
+ render() {
+ const {
+ handleSubmit,
+ language,
+ navigation,
+ skipEndpoint = false,
+ loading
+ } = this.props;
+
+
+ return (
+
+
+ {skipEndpoint ? (
+ this.onBack()}
+ title={Lng.t("header.back", { locale: language })}
+ titleOnPress={() => this.onBack()}
+ titleStyle={{ marginLeft: -10 }}
+ placement="left"
+ noBorder
+ transparent
+ />
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+ this.toggleFocus()
+ }}
+ onFocus={() => this.toggleFocus()}
+ inputContainerStyle={styles.inputField}
+ />
+
+ {Lng.t("endpoint.endpointDesc", { locale: language })}
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
diff --git a/src/features/authentication/components/Endpoint/styles.js b/src/features/authentication/components/Endpoint/styles.js
new file mode 100644
index 00000000..947cede4
--- /dev/null
+++ b/src/features/authentication/components/Endpoint/styles.js
@@ -0,0 +1,50 @@
+import { StyleSheet, Dimensions } from 'react-native';
+import { colors } from '../../../../styles/colors';
+
+const { width } = Dimensions.get('window');
+
+export default StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'flex-start',
+ display: 'flex',
+ paddingHorizontal: 5,
+ },
+ main: {
+ flex: 1,
+ flexDirection: 'column',
+ paddingHorizontal: 25,
+ justifyContent: 'center',
+ },
+ inputField: {
+ // paddingVertical: 3
+ },
+ logoContainer: {
+ paddingBottom: 40,
+ alignItems: 'center',
+ marginTop: -40,
+ },
+ imgLogo: {
+ width: width - 150,
+ height: 120,
+ resizeMode: 'contain',
+ },
+ endpointTextTitle: {
+ marginTop: 15,
+ color: colors.veryDarkGray,
+ },
+ SendingMailContainer: {
+ alignItems: 'center',
+ },
+ buttonContainer: {
+ marginHorizontal: -5,
+ marginTop: 55
+ },
+ buttonStyle: {
+ paddingVertical: 10
+ },
+ skipButtonStyle: {
+ marginTop: 5,
+ marginHorizontal: -5,
+ }
+});
diff --git a/src/features/authentication/components/ForgotPassword/index.js b/src/features/authentication/components/ForgotPassword/index.js
new file mode 100644
index 00000000..17ec2ed9
--- /dev/null
+++ b/src/features/authentication/components/ForgotPassword/index.js
@@ -0,0 +1,168 @@
+// @flow
+
+import React, { Component } from 'react';
+import { View, KeyboardAvoidingView, ScrollView } from 'react-native';
+import { Field } from 'redux-form';
+import styles from './styles';
+import {
+ InputField,
+ AssetImage,
+ CtGradientButton,
+ CtHeader,
+ CtButton,
+} from '../../../../components';
+import { Text } from 'react-native-elements';
+import { IMAGES } from '../../../../config';
+import Lng from '../../../../api/lang/i18n';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+
+type IProps = {
+ navigation: Object,
+ sendForgotPasswordMail: Function,
+ handleSubmit: Function,
+ loading: Boolean,
+ socialLoading: Boolean,
+ language: String,
+}
+export class ForgotPassword extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ email: '',
+ isMailSended: false,
+ };
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ onSendMail = ({ email }) => {
+ const { sendForgotPasswordMail, navigation } = this.props;
+
+ sendForgotPasswordMail({
+ email,
+ navigation,
+ onResult: (val) => {
+ if (val) {
+ this.setState({
+ email,
+ isMailSended: true,
+ });
+ }
+ },
+ });
+ };
+
+ resendMail = () => {
+ const { email } = this.state;
+ this.onSendMail({ email });
+ };
+
+ render() {
+ let passwordInput = {};
+
+ const { handleSubmit, navigation, loading, language } = this.props;
+ const { isMailSended, email } = this.state;
+
+ return (
+
+ {!isMailSended ? (
+ navigation.goBack(null)}
+ title={Lng.t("header.back", { locale: language })}
+ titleOnPress={() => navigation.goBack(null)}
+ titleStyle={{ marginLeft: -10 }}
+ placement="left"
+ noBorder
+ transparent
+ />
+ ) : (
+ navigation.goBack(null)}
+ />
+ )}
+
+
+
+
+
+
+
+
+ {!isMailSended ? (
+
+
+
+ {Lng.t("forgot.emailLabel", { locale: language })}
+
+
+ ) : (
+
+
+
+ {Lng.t("forgot.emailSendDescription", { locale: language })}
+
+
+ )}
+ {!isMailSended ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/authentication/components/ForgotPassword/styles.js b/src/features/authentication/components/ForgotPassword/styles.js
new file mode 100644
index 00000000..319c4869
--- /dev/null
+++ b/src/features/authentication/components/ForgotPassword/styles.js
@@ -0,0 +1,57 @@
+import { StyleSheet, Dimensions } from 'react-native';
+import { colors } from '../../../../styles/colors';
+
+const { width, height } = Dimensions.get('window');
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'flex-start',
+ display: 'flex',
+ paddingHorizontal: 5,
+ },
+ main: {
+ flex: 1,
+ flexDirection: 'column',
+ paddingHorizontal: 25,
+ justifyContent: 'center',
+ },
+ inputField: {
+ paddingVertical: 3
+ },
+ logoContainer: {
+ paddingBottom: 40,
+ alignItems: 'center',
+ marginTop: -40,
+ },
+ imgLogo: {
+ width: width - 150,
+ height: 120,
+ resizeMode: 'contain',
+ },
+ forgotTextTitle: {
+ marginTop: 10,
+ color: colors.veryDarkGray,
+ },
+ SendingMailContainer: {
+ alignItems: 'center',
+ },
+ buttonContainer: {
+ marginHorizontal: -5,
+ marginTop: 55
+ },
+ buttonStyle : {
+ paddingVertical: 10
+ },
+ emailSendTitle: {
+ paddingBottom: 10,
+ color: colors.primary,
+ },
+ emailSendDescription: {
+ paddingHorizontal: 10,
+ fontSize: 14,
+ paddingTop: 18,
+ color: colors.veryDarkGray,
+ textAlign: 'justify',
+ },
+});
diff --git a/src/features/authentication/components/Login/index.js b/src/features/authentication/components/Login/index.js
new file mode 100644
index 00000000..5ee173c3
--- /dev/null
+++ b/src/features/authentication/components/Login/index.js
@@ -0,0 +1,189 @@
+// @flow
+
+import React, { Component } from 'react';
+import {
+ StatusBar,
+ ScrollView,
+ View,
+ KeyboardAvoidingView,
+ Text,
+ TouchableOpacity
+} from 'react-native';
+import { Field } from 'redux-form';
+import styles from './styles';
+import { InputField, CtButton, AssetImage, CtDivider, CtGradientButton } from '../../../../components';
+// import * as Google from 'expo-google-app-auth';
+import { env, IMAGES } from '../../../../config';
+import { colors } from '../../../../styles/colors';
+import { ROUTES } from '../../../../navigation/routes';
+import Lng from '../../../../api/lang/i18n';
+
+type IProps = {
+ navigation: Object,
+ login: Function,
+ handleSubmit: Function,
+ loading: Boolean,
+ socialLoading: Boolean,
+ language: String,
+}
+export class Login extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ /*
+ * Sign in with google
+ onSocialLogin = async () => {
+ const { navigation, socialLogin } = this.props;
+ socialLogin({});
+ try {
+ const result = await Google.logInAsync({
+ androidClientId: env.GOOGLE_ANDROID_CLIENT_ID,
+ iosClientId: env.GOOGLE_IOS_CLIENT_ID,
+ scopes: ['profile', 'email'],
+ });
+
+ if (result.type === 'success') {
+ socialLogin({
+ idToken: result.idToken,
+ navigation,
+ });
+ } else {
+ }
+ } catch (e) {
+ // console.log(e);
+ }
+ }; */
+
+ onLogin = (values) => {
+
+ const { navigation, login } = this.props;
+ login({
+ params: values,
+ navigation,
+ });
+ };
+
+ render() {
+ let passwordInput = {};
+ const {
+ loading,
+ socialLoading,
+ navigation,
+ language,
+ } = this.props;
+
+ let loginRefs = {}
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ loginRefs.password.focus();
+ }
+ }}
+ placeholderColor={colors.white5}
+ inputContainerStyle={styles.inputField}
+ />
+ {
+ passwordInput = ref;
+ }}
+ name="password"
+ component={InputField}
+ inputProps={{
+ returnKeyType: 'go',
+ autoCapitalize: 'none',
+ placeholder: Lng.t("login.password", { locale: language }),
+ autoCorrect: true,
+ onSubmitEditing: this.props.handleSubmit(this.onLogin),
+ }}
+ inputContainerStyle={styles.inputField}
+ secureTextEntry
+ refLinkFn={(ref) => {
+ loginRefs.password = ref;
+ }}
+ />
+
+
+ navigation.navigate(ROUTES.FORGOT_PASSWORD)}
+ >
+
+ {Lng.t("button.forget", { locale: language })}
+
+
+
+
+
+
+
+
+
+
+ {/*
+ * Sign in with google
+
+
+
+
+
+ this.onSocialLogin()}
+ btnTitle={Lng.t("button.singInGoogle", { locale: language })}
+ loading={socialLoading}
+ buttonType={BUTTON_COLOR.WHITE}
+ color={colors.dark3}
+ />
+
+ */}
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/authentication/components/Login/styles.js b/src/features/authentication/components/Login/styles.js
new file mode 100644
index 00000000..193b06d7
--- /dev/null
+++ b/src/features/authentication/components/Login/styles.js
@@ -0,0 +1,44 @@
+import { StyleSheet, Dimensions } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+
+const { width, height } = Dimensions.get('window');
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ paddingHorizontal: 25,
+ justifyContent: 'center',
+ display: 'flex',
+ backgroundColor: colors.veryLightGray
+ },
+ main: {
+ flex: 1,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ },
+ logoContainer: {
+ alignItems: 'center',
+ marginTop: -55,
+ },
+ imgLogo: {
+ width: width - 150,
+ height: 180,
+ resizeMode: 'contain',
+ },
+ forgetPasswordContainer: {
+ flexDirection: 'row',
+ marginTop: 5,
+ marginLeft: 2,
+ marginTop: 8,
+ },
+ forgetPassword: {
+ fontSize: 15,
+ color: colors.primaryLight,
+ fontFamily: fonts.poppinsLight,
+ },
+ socialLoginContainer: {},
+ inputField: {
+ paddingVertical: 5,
+ }
+});
diff --git a/src/features/authentication/constants.js b/src/features/authentication/constants.js
new file mode 100644
index 00000000..fc3646e9
--- /dev/null
+++ b/src/features/authentication/constants.js
@@ -0,0 +1,29 @@
+
+// Forms
+// -----------------------------------------
+export const LOGIN_FORM = 'auth/LOGIN_FORM';
+export const FORGOT_PASSWORD_FORM = 'auth/FORGOT_PASSWORD_FORM';
+export const SET_ENDPOINT_API = 'auth/SET_ENDPOINT_API';
+
+// Actions
+// -----------------------------------------
+export const LOGIN = 'auth/LOGIN';
+export const LOGIN_SUCCESS = 'auth/LOGIN_SUCCESS';
+export const SAVE_ID_TOKEN = 'auth/SAVE_ID_TOKEN';
+export const SOCIAL_LOGIN = 'auth/SOCIAL_LOGIN';
+export const AUTH_TRIGGER_SPINNER = 'auth/AUTH_TRIGGER_SPINNER';
+export const SEND_FORGOT_PASSWORD_MAIL = 'auth/SEND_FORGOT_PASSWORD_MAIL';
+export const GET_BOOTSTRAP = 'auth/GET_BOOTSTRAP';
+export const SET_BOOTSTRAP = 'auth/SET_BOOTSTRAP';
+export const RESET_ID_TOKEN = 'auth/RESET_ID_TOKEN';
+
+export const CHECK_ENDPOINT_API = 'url/CHECK_ENDPOINT_API';
+
+// Endpoint Api URL
+// -----------------------------------------
+
+export const LOGIN_URL = () => 'auth/login'
+export const GET_BOOTSTRAP_URL = () => 'bootstrap'
+export const GET_APP_VERSION_URL = () => 'settings/app/version'
+export const SEND_RECOVERY_MAIL_URL = () => 'auth/password/email'
+export const PING_ENDPOINT_URL = () => `ping`
diff --git a/src/features/authentication/containers/Endpoint/index.js b/src/features/authentication/containers/Endpoint/index.js
new file mode 100644
index 00000000..b0b74097
--- /dev/null
+++ b/src/features/authentication/containers/Endpoint/index.js
@@ -0,0 +1,55 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Endpoint } from '../../components/Endpoint';
+import { reduxForm } from 'redux-form';
+import { SET_ENDPOINT_API } from '../../constants';
+import * as AuthAction from '../../actions';
+import { env } from '../../../../config';
+import { validate } from './validation';
+
+const mapStateToProps = (state, { navigation }) => {
+
+ const {
+ global: { language, endpointURL },
+ auth: { loading }
+ } = state
+
+ let CRATER_URL = (typeof endpointURL !== 'undefined' && endpointURL !== null) ? endpointURL : ''
+
+ let skipEndpoint = navigation.getParam('skipEndpoint', false)
+
+ return {
+ language,
+ skipEndpoint,
+ CRATER_URL,
+ loading: loading && loading.pingEndpointLoading,
+ initialValues: {
+ endpointURL: CRATER_URL,
+ }
+ };
+};
+
+const mapDispatchToProps = {
+ saveEndpointApi: AuthAction.saveEndpointApi,
+ checkEndpointApi: AuthAction.checkEndpointApi
+};
+
+// Redux Forms
+const EndpointReduxForm = reduxForm({
+ form: SET_ENDPOINT_API,
+ validate
+})(Endpoint);
+
+// connect
+const EndpointContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(EndpointReduxForm);
+
+EndpointContainer.navigationOptions = () => ({
+ header: null
+});
+
+export default EndpointContainer;
+
+
diff --git a/src/features/authentication/containers/Endpoint/validation.js b/src/features/authentication/containers/Endpoint/validation.js
new file mode 100644
index 00000000..32e007ff
--- /dev/null
+++ b/src/features/authentication/containers/Endpoint/validation.js
@@ -0,0 +1,10 @@
+import { getError } from "../../../../api/validation";
+
+export const validate = (values) => {
+ const errors = {};
+ const { endpointURL } = values;
+
+ errors.endpointURL = getError(endpointURL, ['requiredField', 'urlFormat']);
+
+ return errors;
+};
diff --git a/src/features/authentication/containers/ForgetPassword/index.js b/src/features/authentication/containers/ForgetPassword/index.js
new file mode 100644
index 00000000..c2dcfc06
--- /dev/null
+++ b/src/features/authentication/containers/ForgetPassword/index.js
@@ -0,0 +1,34 @@
+import { connect } from 'react-redux';
+import { ForgotPassword } from '../../components/ForgotPassword';
+import { validate } from './validation';
+import { reduxForm } from 'redux-form';
+import * as AuthAction from '../../actions';
+import { FORGOT_PASSWORD_FORM } from '../../constants';
+
+const mapStateToProps = ({ auth, global }) => ({
+ loading: auth.loading.forgetPasswordLoading,
+ validation: true,
+ language: global.language,
+});
+
+const mapDispatchToProps = {
+ sendForgotPasswordMail: AuthAction.sendForgotPasswordMail,
+};
+
+// Redux Forms
+const forgotPasswordReduxForm = reduxForm({
+ form: FORGOT_PASSWORD_FORM,
+ validate,
+})(ForgotPassword);
+
+// connect
+const ForgotPasswordContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(forgotPasswordReduxForm);
+
+ForgotPasswordContainer.navigationOptions = {
+ header: null,
+};
+
+export default ForgotPasswordContainer;
diff --git a/src/features/authentication/containers/ForgetPassword/validation.js b/src/features/authentication/containers/ForgetPassword/validation.js
new file mode 100644
index 00000000..ce829a13
--- /dev/null
+++ b/src/features/authentication/containers/ForgetPassword/validation.js
@@ -0,0 +1,10 @@
+import { getError } from "../../../../api/validation";
+
+export const validate = (values) => {
+ const errors = {};
+ const { email } = values;
+
+ errors.email = getError(email, ['required', 'emailFormat']);
+
+ return errors;
+};
diff --git a/src/features/authentication/containers/Login/index.js b/src/features/authentication/containers/Login/index.js
new file mode 100644
index 00000000..5f75ab06
--- /dev/null
+++ b/src/features/authentication/containers/Login/index.js
@@ -0,0 +1,44 @@
+import { connect } from "react-redux";
+import { validate } from './validation';
+import { reduxForm } from 'redux-form';
+import { Login } from "../../components/Login"; //imports the feature's login component.
+import * as AuthAction from '../../actions';
+import { LOGIN_FORM } from "../../constants";
+
+const mapStateToProps = ({
+ auth,
+ global,
+ settings: { account }
+
+}) => ({
+ loading: auth.loading && auth.loading.loginLoading,
+ socialLoading: auth.loading && auth.loading.socialLoginLoading,
+ language: global.language,
+ initialValues: {
+ username: (typeof account !== 'undefined' && account !== null) ? account.email ? account.email : '' : '',
+ }
+});
+
+const mapDispatchToProps = {
+ login: AuthAction.login,
+ socialLogin: AuthAction.socialLogin,
+};
+
+// Redux Forms
+const loginReduxForm = reduxForm({
+ form: LOGIN_FORM,
+ validate,
+})(Login);
+
+// Connects the login-component.
+const LoginContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(loginReduxForm);
+
+LoginContainer.navigationOptions = {
+ header: null,
+};
+
+export default LoginContainer;
+
diff --git a/src/features/authentication/containers/Login/validation.js b/src/features/authentication/containers/Login/validation.js
new file mode 100644
index 00000000..eb4b1bf3
--- /dev/null
+++ b/src/features/authentication/containers/Login/validation.js
@@ -0,0 +1,11 @@
+import { getError } from "../../../../api/validation";
+
+export const validate = (values) => {
+ const errors = {};
+ const { username, password } = values;
+
+ errors.username = getError(username, ['required', 'emailFormat']);
+ errors.password = getError(password, ['required']);
+
+ return errors;
+};
diff --git a/src/features/authentication/containers/index.js b/src/features/authentication/containers/index.js
new file mode 100644
index 00000000..370e6d56
--- /dev/null
+++ b/src/features/authentication/containers/index.js
@@ -0,0 +1,2 @@
+export * from './ForgetPassword'
+export * from './Login'
diff --git a/src/features/authentication/reducers/index.js b/src/features/authentication/reducers/index.js
new file mode 100644
index 00000000..3da117e0
--- /dev/null
+++ b/src/features/authentication/reducers/index.js
@@ -0,0 +1,40 @@
+import { LOGIN_SUCCESS, SAVE_ID_TOKEN, AUTH_TRIGGER_SPINNER, SET_BOOTSTRAP, RESET_ID_TOKEN } from '../constants';
+
+const initialState = {
+ loginError: null,
+ errors: null,
+ idToken: null,
+ expiresIn: null,
+ expoToken: null,
+ bootstrap: {
+ user: null,
+ customers: [],
+ currencies: []
+ },
+ loading: {
+ loginLoading: false,
+ socialLoginLoading: false,
+ forgetPasswordLoading: false,
+ pingEndpointLoading: false
+ }
+};
+
+export default function authReducer(state = initialState, action) {
+ const { payload, type } = action;
+
+ switch (type) {
+ case LOGIN_SUCCESS:
+ return { ...state, ...payload };
+ case SAVE_ID_TOKEN:
+ const { idToken, expiresIn } = payload
+ return { ...state, idToken, expiresIn };
+ case RESET_ID_TOKEN:
+ return { ...state, idToken: null, expiresIn: null };
+ case AUTH_TRIGGER_SPINNER:
+ return { ...state, loading: { ...payload } };
+ case SET_BOOTSTRAP:
+ return { ...state, bootstrap: { ...payload } };
+ default:
+ return state;
+ }
+}
diff --git a/src/features/authentication/saga/index.js b/src/features/authentication/saga/index.js
new file mode 100644
index 00000000..c8444ccf
--- /dev/null
+++ b/src/features/authentication/saga/index.js
@@ -0,0 +1,223 @@
+import { call, put, takeEvery, takeLatest } from 'redux-saga/effects';
+import { saveIdToken, authTriggerSpinner, setBootstrap, getBootstrap, setGlobalBootstrap, globalTriggerSpinner, setAppVersion, saveEndpointApi } from '../actions';
+import {
+ LOGIN,
+ SOCIAL_LOGIN,
+ SEND_FORGOT_PASSWORD_MAIL,
+ GET_BOOTSTRAP,
+ // Endpoint Api URL
+ LOGIN_URL,
+ GET_BOOTSTRAP_URL,
+ SEND_RECOVERY_MAIL_URL,
+ GET_APP_VERSION_URL,
+ CHECK_ENDPOINT_API,
+ PING_ENDPOINT_URL
+} from '../constants';
+import Request from '../../../api/request';
+import { navigateToMainTabs, getTitleByLanguage, navigateRoute } from '../../../navigation/actions';
+import { ROUTES } from '../../../navigation/routes';
+import { setAccountInformation } from '../../settings/actions';
+import { GET_APP_VERSION } from '../../../api/consts';
+import { alertMe } from '../../../api/global';
+import moment from 'moment';
+import { getInvoices } from '../../invoices/actions';
+
+
+
+// Login
+// -----------------------------------------
+function* login(payloadData) {
+ const {
+ payload: { params, navigation },
+ } = payloadData;
+
+ yield put(authTriggerSpinner({ loginLoading: true }));
+
+ try {
+
+ const options = {
+ path: LOGIN_URL(),
+ body: params,
+ isAuthRequired: false
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ if (response.status == 401) {
+ alertMe({ desc: getTitleByLanguage('login.invalid') })
+ return
+ }
+
+ yield put(saveIdToken({
+ idToken: response.access_token,
+ expiresIn: moment().seconds(response.expires_in)
+ }));
+
+ yield put(getBootstrap())
+
+ navigation.navigate(ROUTES.MAIN_INVOICES)
+ } catch (error) {
+ alertMe({ desc: getTitleByLanguage('login.invalid') })
+ } finally {
+ yield put(authTriggerSpinner({ loginLoading: false }));
+ }
+}
+
+function* socialLogin(payloadData) {
+ const {
+ payload: { idToken },
+ } = payloadData;
+
+ try {
+ yield put(authTriggerSpinner({ socialLoginLoading: true }));
+
+ if (idToken) {
+ getBootstrap()
+ yield put(saveIdToken({ idToken }));
+ navigateToMainTabs()
+ }
+ } catch (error) {
+ // console.log(error)
+ } finally {
+ yield put(authTriggerSpinner({ socialLoginLoading: false }));
+ }
+
+}
+
+function* getBootstrapData(payloadData) {
+ const {
+ payload
+ } = payloadData
+
+ try {
+ yield put(authTriggerSpinner({ getBootstrapLoginLoading: true }));
+ yield put(globalTriggerSpinner({ appLoginLoading: true }));
+
+
+ const options = {
+ path: GET_BOOTSTRAP_URL(),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ const { user, customers } = response
+
+ yield put(setAccountInformation({ account: user }));
+
+ yield put(setGlobalBootstrap(response));
+
+ yield put(getInvoices({
+ type: 'UNPAID',
+ onMeta: ({ last_page, current_page }) => {
+ let pagination = { last_page, current_page }
+ navigateRoute(ROUTES.MAIN_INVOICES, { pagination, apiCall: false })
+ }
+ }));
+
+ } catch (error) {
+ // console.log(error)
+ } finally {
+ yield put(authTriggerSpinner({ getBootstrapLoginLoading: false }));
+ yield put(globalTriggerSpinner({ appLoginLoading: false }));
+ }
+
+}
+
+function* getAppVersion(payloadData) {
+ const {
+ payload: { onResult }
+ } = payloadData
+
+ try {
+ yield put(globalTriggerSpinner({ appLoginLoading: true }));
+
+
+ const options = {
+ path: GET_APP_VERSION_URL(),
+ isAuthRequired: false
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setAppVersion(response));
+
+ onResult && onResult(response)
+
+ } catch (error) {
+ // console.log(error)
+ } finally {
+ yield put(globalTriggerSpinner({ appLoginLoading: false }));
+ }
+
+}
+
+// Forget Password
+function* sendRecoveryMail(payloadData) {
+ const {
+ payload: { email, onResult },
+ } = payloadData;
+
+ yield put(authTriggerSpinner({ forgetPasswordLoading: true }));
+
+ try {
+
+ const options = {
+ path: SEND_RECOVERY_MAIL_URL(),
+ body: { email },
+ isAuthRequired: false
+ };
+
+ const res = yield call([Request, 'post'], options);
+
+ res.data ? onResult && onResult(res) :
+ alertMe({ desc: getTitleByLanguage('forgot.emailSendError') })
+
+ } catch (error) {
+ alertMe({ desc: getTitleByLanguage('forgot.emailSendError') })
+ } finally {
+ yield put(authTriggerSpinner({ forgetPasswordLoading: false }));
+ }
+}
+
+function* checkEndpointApi(payloadData) {
+ const {
+ payload: { endpointURL, onResult }
+ } = payloadData
+
+ try {
+ yield put(authTriggerSpinner({ pingEndpointLoading: true }));
+
+ const options = {
+ path: PING_ENDPOINT_URL(),
+ isAuthRequired: false,
+ isPing: `${endpointURL}/api/`
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ let success = true
+ if (response.success === 'crater-self-hosted') {
+ yield put(saveEndpointApi({ endpointURL }));
+ }
+ else {
+ success = false
+ }
+
+ onResult && onResult(success)
+
+ } catch (error) {
+ onResult && onResult(false)
+ } finally {
+ yield put(authTriggerSpinner({ pingEndpointLoading: false }));
+ }
+
+}
+
+export default function* loginSaga() {
+ yield takeEvery(LOGIN, login);
+ yield takeEvery(SOCIAL_LOGIN, socialLogin);
+ yield takeEvery(GET_BOOTSTRAP, getBootstrapData);
+ yield takeEvery(SEND_FORGOT_PASSWORD_MAIL, sendRecoveryMail);
+ yield takeEvery(GET_APP_VERSION, getAppVersion);
+ yield takeEvery(CHECK_ENDPOINT_API, checkEndpointApi);
+}
diff --git a/src/features/customers/actions/index.js b/src/features/customers/actions/index.js
new file mode 100644
index 00000000..355b47cd
--- /dev/null
+++ b/src/features/customers/actions/index.js
@@ -0,0 +1,109 @@
+import {
+ GET_CUSTOMERS,
+ SET_CUSTOMERS,
+ CUSTOMERS_TRIGGER_SPINNER,
+ GET_COUNTRIES,
+ SET_COUNTRIES,
+ GET_STATES,
+ SET_STATES,
+ GET_CITIES,
+ SET_CITIES,
+ CREATE_CUSTOMER,
+ SET_CREATE_CUSTOMER,
+ EDIT_CUSTOMER,
+ SET_EDIT_CUSTOMER,
+ GET_EDIT_CUSTOMER,
+ REMOVE_CUSTOMER,
+ SET_REMOVE_CUSTOMER,
+ SET_FILTER_CUSTOMERS
+} from "../constants";
+
+export const getCustomers = (payload = {}) => ({
+ type: GET_CUSTOMERS,
+ payload,
+});
+
+export const setCustomers = (payload = {}) => ({
+ type: SET_CUSTOMERS,
+ payload,
+});
+
+export const setFilterCustomers = (payload = {}) => ({
+ type: SET_FILTER_CUSTOMERS,
+ payload,
+});
+
+
+export const customerTriggerSpinner = (payload) => ({
+ type: CUSTOMERS_TRIGGER_SPINNER,
+ payload,
+});
+
+export const createCustomer = (payload = {}) => ({
+ type: CREATE_CUSTOMER,
+ payload,
+});
+
+export const setCreateCustomer = (payload = {}) => ({
+ type: SET_CREATE_CUSTOMER,
+ payload,
+});
+
+export const editCustomer = (payload = {}) => ({
+ type: EDIT_CUSTOMER,
+ payload,
+});
+
+export const setEditCustomer = (payload = {}) => ({
+ type: SET_EDIT_CUSTOMER,
+ payload,
+});
+
+export const setRemoveCustomer = (payload) => ({
+ type: SET_REMOVE_CUSTOMER,
+ payload,
+});
+
+export const getEditCustomer = (payload = {}) => ({
+ type: GET_EDIT_CUSTOMER,
+ payload,
+});
+
+export const removeCustomer = (payload = {}) => ({
+ type: REMOVE_CUSTOMER,
+ payload,
+});
+
+
+// Address Country , State , City
+// -----------------------------------------
+
+export const getCountries = (payload = {}) => ({
+ type: GET_COUNTRIES,
+ payload,
+});
+
+export const setCountries = (payload = {}) => ({
+ type: SET_COUNTRIES,
+ payload,
+});
+
+export const getStates = (payload = {}) => ({
+ type: GET_STATES,
+ payload,
+});
+
+export const setStates = (payload = {}) => ({
+ type: SET_STATES,
+ payload,
+});
+
+export const getCities = (payload = {}) => ({
+ type: GET_CITIES,
+ payload,
+});
+
+export const setCities = (payload = {}) => ({
+ type: SET_CITIES,
+ payload,
+});
\ No newline at end of file
diff --git a/src/features/customers/components/Address/index.js b/src/features/customers/components/Address/index.js
new file mode 100644
index 00000000..1595cd5a
--- /dev/null
+++ b/src/features/customers/components/Address/index.js
@@ -0,0 +1,392 @@
+// @flow
+
+import React, { Component } from 'react';
+import { View, TouchableOpacity } from 'react-native';
+import styles from './styles';
+import { Field, change } from 'redux-form';
+import {
+ SlideModal,
+ FakeInput,
+ InputField,
+ CtButton,
+} from '../../../../components';
+import { CUSTOMER_ADDRESS, CUSTOMER_EDIT } from '../../constants';
+import AddressFieldContainer from '../../containers/AddressField';
+import Lng from '../../../../api/lang/i18n';
+import { colors } from '../../../../styles/colors';
+import { MAX_LENGTH } from '../../../../api/global';
+
+type IProps = {
+ label: String,
+ icon: String,
+ onChangeCallback: Function,
+ placeholder: String,
+ containerStyle: Object,
+ rightIcon: String,
+ leftIcon: String,
+ color: String,
+ value: String,
+ items: Object,
+ rightIcon: String,
+ hasBillingAddress: Boolean,
+ meta: Object,
+ handleSubmit: Function,
+ language: String,
+ type: String
+};
+
+let country = 'country_id'
+let state = 'state_id'
+let city = 'city_id'
+
+let addressField = [
+ "name",
+ "address_street_1",
+ "address_street_2",
+ "phone",
+ "zip",
+ "country_id",
+ "state_id",
+ "city_id",
+ "type"
+]
+
+
+export class Address extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ visible: false,
+ values: '',
+ status: false,
+ };
+ }
+
+ componentDidMount() {
+ }
+
+
+ fillShippingAddress = (status) => {
+
+ if (status) {
+ this.setState({ status })
+ const { autoFillValue } = this.props
+
+ if (typeof autoFillValue !== 'undefined') {
+ addressField.map((field) => {
+ this.setFormField(field, autoFillValue[field])
+ })
+ }
+ }
+ else {
+ this.setState({ status })
+ this.clearFormField()
+ }
+ }
+
+ onToggle = () => {
+ const { visible, status } = this.state
+ const { addressValue,
+ hasBillingAddress,
+ autoFillValue,
+ getStates,
+ getCities,
+ type
+ } = this.props
+
+ if (!visible) {
+ if (typeof addressValue !== 'undefined') {
+
+ addressField.map((field) => {
+ this.setFormField(field, addressValue[field])
+ })
+
+ if (type === CUSTOMER_EDIT) {
+ addressValue.country_id && getStates({ countryId: addressValue.country_id })
+
+ addressValue.state_id && getCities({ stateID: addressValue.state_id })
+ }
+
+ }
+
+ if (!hasBillingAddress && status === true && typeof addressValue === 'undefined') {
+ if (typeof autoFillValue !== 'undefined') {
+ addressField.map((field) => {
+ this.setFormField(field, autoFillValue[field])
+ })
+ }
+ }
+ }
+ else {
+ if (typeof addressValue === 'undefined')
+ this.clearFormField()
+ }
+ this.setState((prevState) => {
+ return { visible: !prevState.visible }
+ });
+ }
+
+ setFormField = (field, value) => {
+
+ this.props.dispatch(change(CUSTOMER_ADDRESS, field, value));
+ };
+
+ clearFormField = () => {
+ addressField.map((field) => {
+ this.setFormField(field, "")
+ })
+ };
+
+ saveAddress = (address) => {
+ const { onChangeCallback } = this.props
+ this.onToggle()
+
+ onChangeCallback(address)
+ this.clearFormField()
+ }
+
+ BOTTOM_ACTION = (handleSubmit) => {
+ const { language } = this.props
+ return (
+
+
+
+
+
+ )
+ }
+
+ Screen = () => {
+ const {
+ handleSubmit,
+ hasBillingAddress,
+ navigation,
+ addressValue,
+ formValues,
+ language,
+ } = this.props
+
+ const { status } = this.state
+
+ let selectedCountry = ''
+ let selectedState = ''
+ let selectedCity = ''
+
+ if (hasBillingAddress && typeof addressValue !== 'undefined') {
+ let { country_id, state_id, city_id } = addressValue
+
+ selectedCountry = country_id ? country_id : null
+ selectedState = state_id ? state_id : null
+ selectedCity = city_id ? city_id : null
+ }
+
+ if (!hasBillingAddress && typeof formValues !== 'undefined') {
+ let { country_id, state_id, city_id } = formValues
+
+ selectedCountry = country_id ? country_id : null
+ selectedState = state_id ? state_id : null
+ selectedCity = city_id ? city_id : null
+ }
+
+ let addressRefs = {}
+
+ return (
+
+
+ {!hasBillingAddress && (
+ this.fillShippingAddress(!status)
+ }
+ />
+ )}
+
+
+
+
+ this.setFormField(country, val)
+ }
+ />
+
+
+ this.setFormField(state, val)
+ }
+ />
+
+ {
+ this.setFormField(city, val)
+ }}
+ />
+
+ {
+ addressRefs.street1 = ref;
+ }}
+ />
+
+
+
+ {
+ addressRefs.phone = ref;
+ }}
+ />
+
+
+
+ )
+ }
+
+ render() {
+ const {
+ containerStyle,
+ label,
+ icon,
+ placeholder,
+ meta,
+ rightIcon,
+ hasBillingAddress,
+ handleSubmit,
+ language,
+ type,
+ fakeInputProps,
+ } = this.props;
+
+
+
+ const {
+ visible,
+ values,
+ } = this.state
+
+ return (
+
+
+
+ this.onToggle(),
+ title: hasBillingAddress ? Lng.t("header.billingAddress", { locale: language }) : Lng.t("header.shippingAddress", { locale: language }),
+ placement: "center",
+ hasCircle: false,
+ noBorder: false,
+ transparent: false,
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ >
+ {this.Screen()}
+
+
+
+ );
+ }
+}
diff --git a/src/features/customers/components/Address/styles.js b/src/features/customers/components/Address/styles.js
new file mode 100644
index 00000000..29f28d0c
--- /dev/null
+++ b/src/features/customers/components/Address/styles.js
@@ -0,0 +1,27 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { isIPhoneX } from '../../../../api/helper';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ submitButton: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ },
+ handleBtn: {
+ marginHorizontal: 9,
+ },
+ addressStreetField: {
+ marginTop: -20,
+ },
+ sameAsToggle: {
+ color: colors.primary
+ },
+});
diff --git a/src/features/customers/components/AddressField/index.js b/src/features/customers/components/AddressField/index.js
new file mode 100644
index 00000000..344dd2ea
--- /dev/null
+++ b/src/features/customers/components/AddressField/index.js
@@ -0,0 +1,311 @@
+// @flow
+
+import React, { Component } from 'react';
+import { View } from 'react-native';
+import { change } from 'redux-form';
+import styles from './styles';
+import { SlideModal, FakeInput } from '../../../../components';
+import Lng from '../../../../api/lang/i18n';
+import { colors } from '../../../../styles/colors';
+
+type IProps = {
+ label: String,
+ icon: String,
+ onChangeCallback: Function,
+ placeholder: String,
+ containerStyle: Object,
+ leftIcon: String,
+ color: String,
+ value: String,
+ items: Object,
+ type: String,
+ isRequired: Boolean,
+};
+
+let COUNTRY = 'country'
+let STATE = 'state'
+let CITY = 'city'
+
+
+export class AddressField extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ newAddressValue: [],
+ found: true,
+ visible: false,
+ values: '',
+ search: '',
+ };
+
+ }
+
+ onToggle = () => {
+
+ const { search } = this.state
+
+ this.setState((prevState) => {
+ return {
+ visible: !prevState.visible,
+ newAddressValue: [],
+ search: ''
+ }
+ });
+
+ if (search)
+ this.props.meta.dispatch(change(this.props.meta.form, 'search', ''));
+
+ }
+
+ onSelect = (item) => {
+
+ const {
+ onChangeCallback,
+ type,
+ getStates,
+ getCities
+ } = this.props
+
+ let { id, name } = item
+
+ if (type === COUNTRY) {
+ this.setState({
+ values: `${name} (${item.code})`
+ })
+
+ this.onToggle()
+ onChangeCallback(id)
+ getStates({ countryId: id })
+ }
+ else if (type === STATE) {
+ this.setState({ values: `${name}` })
+
+ this.onToggle()
+ onChangeCallback(id)
+ getCities({ stateID: id })
+ }
+ else if (type === CITY) {
+ this.setState({ values: `${name}` })
+
+ this.onToggle()
+ onChangeCallback(id)
+ }
+
+ }
+
+ onSearch = (search) => {
+
+ const {
+ type,
+ countries,
+ states,
+ cities,
+ } = this.props
+
+ let items = []
+
+ if (type === COUNTRY)
+ items = countries
+ else if (type === STATE)
+ items = states
+ else if (type === CITY)
+ items = cities
+
+ if (typeof items !== 'undefined' && items.length != 0) {
+ let newData = items.filter((item) => {
+ const items = item.name.toUpperCase()
+ const searchName = search.toUpperCase()
+ return items.indexOf(searchName) > -1
+ });
+
+ let newAddressValue = this.itemList(newData)
+
+ this.setState({
+ newAddressValue,
+ found: newAddressValue.length != 0 ? true : false,
+ search
+ })
+ }
+
+ }
+
+ selectedField = (id) => {
+ const { countries, states, cities, type } = this.props
+ let items = []
+ let value = ''
+ if (id && id !== null && typeof id !== 'undefined') {
+ if (type === COUNTRY)
+ items = countries
+ else if (type === STATE)
+ items = states
+ else if (type === CITY)
+ items = cities
+
+ if (typeof items !== 'undefined' && items.length != 0) {
+ const newData = items.filter((item) => {
+ const items = item.id
+ return items === id
+ });
+
+ if (newData.length !== 0) {
+ let { name } = newData[0]
+ if (type === COUNTRY) {
+ let { code } = newData[0]
+ value = `${name} (${code})`
+ }
+ else
+ value = name
+ }
+ }
+ }
+
+ return value
+ }
+
+ itemList = (items) => {
+ const { type } = this.props
+ let itemList = []
+
+ if (typeof items !== 'undefined' && items.length != 0) {
+ if (type === COUNTRY) {
+ itemList = items.map((item) => {
+ const { name, code } = item
+ return {
+ title: name,
+ rightTitle: code,
+ fullItem: item
+ }
+ })
+ }
+ else if (type === STATE || type === CITY) {
+ itemList = items.map((item) => {
+ const { name } = item
+ return {
+ title: name,
+ fullItem: item
+ }
+ })
+ }
+
+ }
+ return itemList
+ }
+
+
+ render() {
+ const {
+ containerStyle,
+ loading,
+ label,
+ icon,
+ placeholder,
+ meta,
+ type,
+ countries,
+ states,
+ cities,
+ selectedCountry,
+ selectedState,
+ selectedCity,
+ countriesLoading,
+ statesLoading,
+ citiesLoading,
+ language,
+ placeholderStyle,
+ isRequired,
+ } = this.props;
+
+ const {
+ visible,
+ values,
+ search,
+ newAddressValue,
+ found,
+ } = this.state
+
+ let itemList = []
+ let selectedValue = ''
+ let title = ''
+ let empty = ''
+ let fakeInputLoading = false
+
+
+ if (type === COUNTRY) {
+ itemList = this.itemList(countries)
+ selectedValue = this.selectedField(selectedCountry)
+ title = Lng.t("header.country", { locale: language })
+ empty = Lng.t("customers.empty.country.title", { locale: language })
+ fakeInputLoading = countriesLoading
+ }
+ else if (type === STATE) {
+ itemList = this.itemList(states)
+ selectedValue = this.selectedField(selectedState)
+ title = Lng.t("header.state", { locale: language })
+ empty = Lng.t("customers.empty.state.title", { locale: language })
+ fakeInputLoading = statesLoading
+ }
+ else if (type === CITY) {
+ itemList = this.itemList(cities)
+ selectedValue = this.selectedField(selectedCity)
+ title = Lng.t("header.city", { locale: language })
+ empty = Lng.t("customers.empty.city.title", { locale: language })
+ fakeInputLoading = citiesLoading
+ }
+
+ return (
+
+
+
+ this.onToggle(),
+ title: title,
+ titleStyle: styles.headerTitle,
+ placement: "center",
+ hasCircle: false,
+ noBorder: false,
+ transparent: false,
+ }}
+ onSearch={this.onSearch}
+ searchInputProps={{
+ autoFocus: true
+ }}
+ bottomDivider
+ listViewProps={{
+ items: newAddressValue.length != 0 ? newAddressValue
+ : found ? itemList : [],
+ onPress: this.onSelect,
+ loading: loading,
+ isEmpty: found ? itemList.length <= 0 : true,
+ bottomDivider: true,
+ emptyContentProps: {
+ title: found ? empty :
+ Lng.t("search.noResult", { locale: language, search }),
+ },
+ contentContainerStyle: { flex: 7 }
+ }}
+ />
+
+ );
+ }
+}
diff --git a/src/features/customers/components/AddressField/styles.js b/src/features/customers/components/AddressField/styles.js
new file mode 100644
index 00000000..7b65388b
--- /dev/null
+++ b/src/features/customers/components/AddressField/styles.js
@@ -0,0 +1,11 @@
+import { StyleSheet } from 'react-native';
+
+export default StyleSheet.create({
+ container: {
+ flex: 1,
+ marginTop: 10,
+ },
+ addressValueStyle: {
+ paddingLeft: 10,
+ }
+});
diff --git a/src/features/customers/components/Customer/index.js b/src/features/customers/components/Customer/index.js
new file mode 100644
index 00000000..c4b128c2
--- /dev/null
+++ b/src/features/customers/components/Customer/index.js
@@ -0,0 +1,476 @@
+// @flow
+
+import React from 'react';
+import { View, Alert } from 'react-native';
+import styles from './styles';
+import { Field, change } from 'redux-form';
+import {
+ InputField,
+ CtButton,
+ CtDivider,
+ ToggleSwitch,
+ DefaultLayout,
+ SelectField,
+} from '../../../../components';
+import { CUSTOMER_FORM, CUSTOMER_EDIT, CUSTOMER_ADD, CUSTOMER_ACTIONS, ACTIONS_VALUE } from '../../constants';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import AddressContainer from '../../containers/Address';
+import Lng from '../../../../api/lang/i18n';
+import { colors } from '../../../../styles/colors';
+import { SymbolStyle } from '../../../../components/CurrencyFormat/styles';
+import { headerTitle } from '../../../../api/helper';
+
+let customerField = [
+ "name",
+ "contact_name",
+ "email",
+ "phone",
+ "website",
+ "enable_portal",
+]
+
+
+type IProps = {
+ navigation: Object,
+ type: String,
+ getEditCustomer: Function,
+ createCustomer: Function,
+ editCustomer: Function,
+ handleSubmit: Function,
+ customerLoading: Boolean,
+ getEditCustomerLoading: Boolean,
+ language: String,
+ formValues: Object,
+}
+
+export class Customer extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ selectedCurrency: '',
+ portal: false,
+ currencyList: [],
+ };
+
+ }
+
+ componentDidMount() {
+
+ const {
+ navigation,
+ getEditCustomer,
+ type,
+ currencies,
+ getCountries,
+ countries
+ } = this.props
+
+ this.setState({ currencyList: currencies })
+
+ // Country
+ let hasCountryApiCalled = countries ? (typeof countries === 'undefined' || countries.length === 0) : true
+
+ hasCountryApiCalled && getCountries()
+
+ if (type === CUSTOMER_EDIT) {
+
+ let id = navigation.getParam('customerId')
+
+ getEditCustomer({
+ id,
+ onResult: (customer) => {
+ // let { currency_id, enable_portal } = customer
+ let { currency_id } = customer
+
+ customerField.map((field) => {
+ this.setFormField(field, customer[field])
+ })
+
+ this.setFormField('billingAddress',
+ customer.billing_address ?
+ customer.billing_address : []
+ )
+ this.setFormField('shippingAddress',
+ customer.shipping_address ?
+ customer.shipping_address : []
+ )
+
+ if (currency_id) {
+ let { name, code } = customer.currency
+ this.setFormField('currency_id',
+ customer.currency ?
+ customer.currency.id : null)
+ this.setState({ selectedCurrency: `${name}` })
+ }
+ // this.setState({ portal: enable_portal === 1 ? true : false })
+ }
+ });
+ }
+ else {
+ if (typeof currencies !== 'undefined' && currencies.length != 0) {
+ let { name, id } = currencies[0]
+ this.setFormField('currency_id', id)
+ this.setState({ selectedCurrency: `${name}` })
+ }
+ }
+
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(CUSTOMER_FORM, field, value));
+ };
+
+ onTogglePortal = (status) => {
+
+ this.setFormField('enable_portal', status)
+ this.setState({ portal: status })
+
+ if (!status)
+ this.setFormField('password', '')
+ }
+
+ onCustomerSubmit = (values) => {
+ const {
+ type,
+ createCustomer,
+ editCustomer,
+ navigation
+ } = this.props
+
+ if (type === CUSTOMER_ADD)
+ createCustomer({
+ params: values,
+ onResult: (res) => {
+ const onSelect = navigation.getParam('onSelect', null)
+ onSelect && onSelect(res)
+ navigation.goBack(null)
+ }
+ })
+ else
+ editCustomer({ params: values, navigation })
+ };
+
+ removeCustomer = () => {
+ const { removeCustomer, navigation, language } = this.props
+
+ Alert.alert(
+ Lng.t("alert.title", { locale: language }),
+ Lng.t("customers.alertDescription", { locale: language }),
+ [
+ {
+ text: 'OK',
+ onPress: () => removeCustomer({
+ id: navigation.getParam('customerId', null),
+ navigation
+ })
+ },
+ {
+ text: 'Cancel',
+ onPress: () => console.log('cancel'),
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ }
+
+
+ BOTTOM_ACTION = (handleSubmit) => {
+ const {
+ customerLoading,
+ language,
+ type
+ } = this.props
+
+ let buttonTitle = Lng.t("button.save", { locale: language })
+
+ return (
+
+
+
+ )
+ }
+
+ getCurrenciesList = (currencies) => {
+ let currencyList = []
+ if (typeof currencies !== 'undefined' && currencies.length != 0) {
+ currencyList = currencies.map((currency) => {
+
+ const { name, code, symbol } = currency
+ return {
+ title: name,
+ subtitle: {
+ title: code,
+ },
+ rightTitle: symbol || '-',
+ fullItem: currency
+ }
+ })
+ }
+ return currencyList
+ }
+
+ onOptionSelect = (action) => {
+
+ if (action == ACTIONS_VALUE.REMOVE)
+ this.removeCustomer()
+
+ }
+
+ render() {
+ const {
+ navigation,
+ handleSubmit,
+ formValues: {
+ billingAddress,
+ shippingAddress,
+ enable_portal,
+ currency_id
+ },
+ language,
+ getEditCustomerLoading,
+ countriesLoading,
+ type
+ } = this.props;
+
+ const { selectedCurrency, portal, currencyList } = this.state
+ let drownDownProps = type === CUSTOMER_EDIT ? {
+ options: CUSTOMER_ACTIONS(Lng, language),
+ onSelect: this.onOptionSelect,
+ cancelButtonIndex: 1,
+ destructiveButtonIndex: 2,
+ } : null
+
+ let customerRefs = {}
+
+ return (
+ navigation.goBack(null),
+ title: type === CUSTOMER_EDIT ?
+ Lng.t("header.editCustomer", { locale: language }) :
+ Lng.t("header.addCustomer", { locale: language }),
+ titleStyle: styles.headerTitle,
+ placement: "center",
+ rightIcon: type !== CUSTOMER_EDIT ? "save" : null,
+ rightIconProps: {
+ solid: true,
+ },
+ rightIconPress: handleSubmit(this.onCustomerSubmit),
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ loadingProps={{
+ is: getEditCustomerLoading || typeof enable_portal === 'undefined' || countriesLoading
+ }}
+ dropdownProps={drownDownProps}
+ >
+
+
+ {
+ customerRefs.contactName.focus();
+ }
+ }}
+ validationStyle={styles.inputFieldValidation}
+ />
+
+ {
+ customerRefs.email.focus();
+ }
+ }}
+ refLinkFn={(ref) => {
+ customerRefs.contactName = ref;
+ }}
+ validationStyle={styles.inputFieldValidation}
+ />
+
+ {
+ customerRefs.phone.focus();
+ }
+ }}
+ refLinkFn={(ref) => {
+ customerRefs.email = ref;
+ }}
+ validationStyle={styles.inputFieldValidation}
+ />
+
+ {
+ customerRefs.website.focus();
+ }
+ }}
+ refLinkFn={(ref) => {
+ customerRefs.phone = ref;
+ }}
+ validationStyle={styles.inputFieldValidation}
+ />
+
+ {
+ customerRefs.website = ref;
+ }}
+ validationStyle={styles.inputFieldValidation}
+ />
+
+
+
+
+ {
+ this.setFormField('currency_id', val.id)
+ }}
+ headerProps={{
+ title: Lng.t("currencies.title", { locale: language }),
+ titleStyle: headerTitle({ marginLeft: -30, marginRight: -65 }),
+ rightIconPress: null
+ }}
+ listViewProps={{
+ contentContainerStyle: { flex: 5 },
+ rightTitleStyle: SymbolStyle
+ }}
+ emptyContentProps={{
+ contentType: "currencies",
+ }}
+ />
+
+
+ this.setFormField('billingAddress', val)
+ }
+ containerStyle={styles.addressField}
+ type={type}
+ fakeInputProps={{
+ color: billingAddress && (Object.keys(billingAddress).length !== 0) ? colors.primaryLight : null,
+ }}
+ />
+
+
+ this.setFormField('shippingAddress', val)
+ }
+ containerStyle={styles.addressField}
+ type={type}
+ fakeInputProps={{
+ color: shippingAddress && (Object.keys(shippingAddress).length !== 0) ? colors.primaryLight : null,
+ }}
+ />
+
+
+
+ {/*
+
+
+
+ this.onTogglePortal(val)
+ }
+ />
+
+ {portal && (
+
+ )} */}
+
+
+
+ );
+ }
+}
diff --git a/src/features/customers/components/Customer/styles.js b/src/features/customers/components/Customer/styles.js
new file mode 100644
index 00000000..5a4a55f4
--- /dev/null
+++ b/src/features/customers/components/Customer/styles.js
@@ -0,0 +1,49 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ leftIcon: {
+ color: colors.dark,
+ },
+ inputGroup: {
+ marginVertical: 10,
+ },
+ divider: {
+ backgroundColor: colors.lightGray,
+ borderColor: colors.lightGray,
+ borderWidth: 0.7,
+ marginTop: 10,
+ marginBottom: 8,
+ },
+ dividerStyle: {
+ borderBottomWidth: 0.5,
+ borderColor: colors.lightGray,
+ marginTop: 3,
+ },
+ addressField: {
+ marginTop: -10,
+ borderTopWidth: 0,
+ },
+ submitButton: {
+ paddingHorizontal: 10,
+ },
+ multipleButton: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ flexDirection: 'row'
+ },
+ btnContainerStyle: {
+ marginHorizontal: 10,
+ },
+ flex: {
+ flex: 1
+ },
+});
diff --git a/src/features/customers/components/Customers/index.js b/src/features/customers/components/Customers/index.js
new file mode 100644
index 00000000..2e09da13
--- /dev/null
+++ b/src/features/customers/components/Customers/index.js
@@ -0,0 +1,325 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import styles from './styles';
+import { ListView, MainLayout } from '../../../../components';
+import { IMAGES } from '../../../../config';
+import { ROUTES } from '../../../../navigation/routes';
+import Lng from '../../../../api/lang/i18n';
+import { CUSTOMER_ADD, CUSTOMER_EDIT } from '../../constants';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+
+
+type IProps = {
+ customers: Object,
+ navigation: Object,
+ loading: Boolean,
+ language: String,
+}
+
+let params = {
+ search: '',
+ display_name: '',
+ contact_name: '',
+ phone: '',
+}
+
+export class Customers extends React.Component {
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ refreshing: false,
+ fresh: true,
+ pagination: {
+ page: 1,
+ limit: 10,
+ lastPage: 1,
+ },
+ search: '',
+ filter: false,
+ };
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation, ROUTES.MAIN_INVOICES)
+ }
+
+ componentWillUpdate(nextProps, nextState) {
+
+ const { navigation } = nextProps
+ const pagination = navigation.getParam('pagination', null)
+ const apiCall = navigation.getParam('apiCall', false)
+
+ if (pagination && !(apiCall)) {
+ navigation.setParams({ 'pagination': null, apiCall: true })
+
+ const { last_page, current_page } = pagination
+ this.setState({
+ pagination: {
+ ...this.state.pagination,
+ lastPage: last_page,
+ page: current_page + 1,
+ }
+ });
+ }
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ getItems = ({
+ fresh = false,
+ params,
+ onResult,
+ filter = false
+ } = {}) => {
+
+ const { getCustomer } = this.props;
+ const { refreshing, pagination } = this.state;
+
+ if (refreshing) {
+ return;
+ }
+
+ this.setState({
+ refreshing: true,
+ fresh,
+ });
+
+ const paginationParams = fresh ? { ...pagination, page: 1 } : pagination;
+
+ if (!fresh && paginationParams.lastPage < paginationParams.page) {
+ return;
+ }
+
+ getCustomer({
+ fresh,
+ pagination: paginationParams,
+ params,
+ filter,
+ onMeta: ({ last_page, current_page }) => {
+ this.setState({
+ pagination: {
+ ...paginationParams,
+ lastPage: last_page,
+ page: current_page + 1,
+ },
+ });
+ },
+ onResult: (val) => {
+ this.setState({
+ refreshing: false,
+ fresh: !val,
+ });
+ onResult && onResult();
+ },
+ });
+ };
+
+ onSearch = (search) => {
+ this.onResetFilter()
+ this.setState({ search })
+ this.getItems({ fresh: true, params: { ...params, search } })
+ };
+
+ onResetFilter = () => {
+ this.setState({ filter: false })
+ }
+
+ onSubmitFilter = ({ name = '', contact_name = '', phone = '' }) => {
+
+ if (name || contact_name || phone) {
+ this.setState({ filter: true })
+
+ this.getItems({
+ fresh: true,
+ params: {
+ ...params,
+ display_name: name,
+ contact_name,
+ phone
+ },
+ filter: true
+ })
+ }
+ else
+ this.onResetFilter()
+ }
+
+ onCustomerSelect = (customer) => {
+ const { navigation } = this.props
+ navigation.navigate(ROUTES.CUSTOMER,
+ { customerId: customer.id, type: CUSTOMER_EDIT }
+ )
+ this.onResetFilter()
+ }
+
+ loadMoreItems = () => {
+ const { search, filter } = this.state
+
+ const {
+ formValues: {
+ name = '',
+ contact_name = '',
+ phone = ''
+ }
+
+ } = this.props
+
+ if (filter) {
+ this.getItems({
+ q: name,
+ params: {
+ ...params,
+ display_name: name,
+ contact_name,
+ phone
+ },
+ contact_name,
+ phone,
+ filter: true
+ })
+ }
+ else
+ this.getItems({ params: { ...params, search } });
+ }
+
+
+ render() {
+ const {
+ customers,
+ filterCustomers,
+ navigation,
+ loading,
+ language,
+ handleSubmit
+ } = this.props
+
+ let filterRefs = {}
+
+ const {
+ refreshing,
+ pagination: { lastPage, page },
+ fresh,
+ search,
+ filter,
+ } = this.state;
+
+ const canLoadMore = lastPage >= page;
+
+ let inputFields = [
+ {
+ name: 'name',
+ hint: Lng.t("customers.filterDisplayName", { locale: language }),
+ inputProps: {
+ autoCorrect: true,
+ autoFocus: true,
+ onSubmitEditing: () => {
+ filterRefs.contactName.focus();
+ }
+ }
+ },
+ {
+ name: 'contact_name',
+ hint: Lng.t("customers.filterContactName", { locale: language }),
+ inputProps: {
+ autoCorrect: true,
+ onSubmitEditing: () => {
+ filterRefs.phone.focus();
+ }
+ },
+ refLinkFn: (ref) => {
+ filterRefs.contactName = ref;
+ }
+ },
+ {
+ name: 'phone',
+ hint: Lng.t("customers.phone", { locale: language }),
+ inputProps: {
+ keyboardType: 'phone-pad'
+ },
+ refLinkFn: (ref) => {
+ filterRefs.phone = ref;
+ }
+ }
+ ]
+
+ let empty = (!filter && !search) ? {
+ description: Lng.t("customers.empty.description", { locale: language }),
+ buttonTitle: Lng.t("customers.empty.buttonTitle", { locale: language }),
+ buttonPress: () => {
+ navigation.navigate(ROUTES.CUSTOMER, { type: CUSTOMER_ADD })
+ this.onResetFilter()
+ }
+ } : {}
+
+ let emptyTitle = search ? Lng.t("search.noResult", { locale: language, search })
+ : (!filter) ? Lng.t("customers.empty.title", { locale: language }) :
+ Lng.t("filter.empty.filterTitle", { locale: language })
+
+ let isLoading = navigation.getParam('loading', false)
+
+ return (
+
+ {
+ navigation.navigate(ROUTES.CUSTOMER, { type: CUSTOMER_ADD })
+ this.onResetFilter()
+ },
+ title: Lng.t("header.customers", { locale: language })
+ }}
+ onSearch={this.onSearch}
+ filter
+ filterProps={{
+ onSubmitFilter: handleSubmit(this.onSubmitFilter),
+ inputFields: inputFields,
+ clearFilter: this.props,
+ language: language
+ }}
+ bottomDivider
+ loadingProps={{ is: isLoading || (loading && fresh) }}
+ >
+
+
+ {
+ this.onResetFilter()
+ this.getItems({
+ fresh: true,
+ onResult: onHide,
+ params: { ...params, search }
+ });
+ }}
+ getItems={() => {
+ this.loadMoreItems()
+ }}
+ bottomDivider
+ hasAvatar
+ emptyContentProps={{
+ title: emptyTitle,
+ image: IMAGES.EMPTY_CUSTOMERS,
+ ...empty
+ }}
+ />
+
+
+
+
+ );
+ }
+}
+
diff --git a/src/features/customers/components/Customers/styles.js b/src/features/customers/components/Customers/styles.js
new file mode 100644
index 00000000..0e72a289
--- /dev/null
+++ b/src/features/customers/components/Customers/styles.js
@@ -0,0 +1,27 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ listViewContainer: {
+ flex: 1,
+ },
+ Tabs: {
+ backgroundColor: colors.lightGray,
+ borderBottomRightRadius: 10,
+ borderBottomLeftRadius: 10,
+ },
+ headerTitle: {
+ fontSize: 30,
+ color: colors.dark2,
+ fontWeight: '600',
+ },
+ inputField: {
+ paddingHorizontal: 19,
+ paddingVertical: 0,
+ marginVertical: 8,
+ },
+});
diff --git a/src/features/customers/constants.js b/src/features/customers/constants.js
new file mode 100644
index 00000000..a610c9f9
--- /dev/null
+++ b/src/features/customers/constants.js
@@ -0,0 +1,77 @@
+import React, { Component } from 'react';
+import queryString from 'query-string';
+
+// Forms
+// -----------------------------------------
+export const CUSTOMER_SEARCH = 'customers/CUSTOMER_SEARCH';
+export const CUSTOMER_FORM = 'customers/CUSTOMER_FORM';
+export const CUSTOMER_ADDRESS = 'customers/CUSTOMER_ADDRESS';
+
+// Type
+// -----------------------------------------
+export const CUSTOMER_ADD = 'customers/CUSTOMER_ADD';
+export const CUSTOMER_EDIT = 'customers/CUSTOMER_EDIT';
+
+// Actions
+// -----------------------------------------
+export const GET_CUSTOMERS = 'customers/GET_CUSTOMERS';
+export const SET_CUSTOMERS = 'customers/SET_CUSTOMERS';
+export const SET_FILTER_CUSTOMERS = 'customers/SET_FILTER_CUSTOMERS';
+export const CREATE_CUSTOMER = 'customers/CREATE_CUSTOMER';
+export const SET_CREATE_CUSTOMER = 'customers/SET_CREATE_CUSTOMER';
+export const EDIT_CUSTOMER = 'customers/EDIT_CUSTOMER';
+export const SET_EDIT_CUSTOMER = 'customers/SET_EDIT_CUSTOMER';
+export const SET_REMOVE_CUSTOMER = 'customers/SET_REMOVE_CUSTOMER';
+export const GET_EDIT_CUSTOMER = 'customers/GET_EDIT_CUSTOMER';
+
+export const CUSTOMERS_TRIGGER_SPINNER = 'customers/CUSTOMERS_TRIGGER_SPINNER';
+export const REMOVE_CUSTOMER = 'customers/REMOVE_CUSTOMER';
+
+
+// Address Country , State , City
+// -----------------------------------------
+
+export const GET_COUNTRIES = 'address/GET_COUNTRIES';
+export const SET_COUNTRIES = 'address/SET_COUNTRIES';
+
+export const GET_STATES = 'address/GET_STATES';
+export const SET_STATES = 'address/SET_STATES';
+
+export const GET_CITIES = 'address/GET_CITIES';
+export const SET_CITIES = 'address/SET_CITIES';
+
+
+export const ACTIONS_VALUE = {
+ REMOVE: 'remove',
+
+}
+
+export const CUSTOMER_ACTIONS = (Lng, language) => {
+ return [
+ {
+ label: Lng.t("customers.removeCustomer", { locale: language })
+ ,
+ value: ACTIONS_VALUE.REMOVE
+ }
+ ];
+}
+
+
+// Endpoint Api URL
+// -----------------------------------------
+
+export const GET_CUSTOMERS_URL = (param) => `customers?${queryString.stringify({
+ ...param,
+ orderByField: 'created_at',
+ orderBy: 'desc'
+})}`
+
+export const GET_EDIT_CUSTOMER_URL = (id) => `customers/${id}/edit`
+
+export const GET_COUNTRIES_URL = () => `countries`
+export const GET_STATES_URL = (countryId) => `states/${countryId}`
+export const GET_CITIES_URL = (stateID) => `cities/${stateID}`
+
+export const CREATE_CUSTOMER_URL = () => `customers`
+export const EDIT_CUSTOMER_URL = (id) => `customers/${id}`
+export const REMOVE_CUSTOMER_URL = (id) => `customers/${id}`
diff --git a/src/features/customers/containers/Address/index.js b/src/features/customers/containers/Address/index.js
new file mode 100644
index 00000000..5b8fba8d
--- /dev/null
+++ b/src/features/customers/containers/Address/index.js
@@ -0,0 +1,41 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { reduxForm, getFormValues } from 'redux-form';
+import { CUSTOMER_ADDRESS } from '../../constants';
+import { Address } from '../../components/Address';
+import * as AddressAction from '../../actions'
+
+const mapStateToProps = (state) => {
+ const {
+ global: { language }
+ } = state
+
+ return {
+ formValues: getFormValues(CUSTOMER_ADDRESS)(state) || {},
+ language
+ };
+};
+
+const mapDispatchToProps = {
+ getCountries: AddressAction.getCountries,
+ getStates: AddressAction.getStates,
+ getCities: AddressAction.getCities,
+};
+
+// Redux Forms
+const addressReduxForm = reduxForm({
+ form: CUSTOMER_ADDRESS,
+})(Address);
+
+// connect
+const AddressContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(addressReduxForm);
+
+AddressContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default AddressContainer;
+
diff --git a/src/features/customers/containers/AddressField/index.js b/src/features/customers/containers/AddressField/index.js
new file mode 100644
index 00000000..a814f1fe
--- /dev/null
+++ b/src/features/customers/containers/AddressField/index.js
@@ -0,0 +1,30 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { AddressField } from '../../components/AddressField';
+import * as AddressAction from '../../actions'
+
+const mapStateToProps = ({ customers, global }) => ({
+ countries: customers.countries,
+ countriesLoading: customers.loading.countriesLoading,
+ states: customers.states,
+ statesLoading: customers.loading.statesLoading,
+ cities: customers.cities,
+ citiesLoading: customers.loading.citiesLoading,
+ language: global.language,
+});
+
+const mapDispatchToProps = {
+ getCountries: AddressAction.getCountries,
+ getStates: AddressAction.getStates,
+ getCities: AddressAction.getCities,
+};
+
+
+// connect
+const AddressFieldContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(AddressField);
+
+
+export default AddressFieldContainer;
diff --git a/src/features/customers/containers/Customer/index.js b/src/features/customers/containers/Customer/index.js
new file mode 100644
index 00000000..0ed766d4
--- /dev/null
+++ b/src/features/customers/containers/Customer/index.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { reduxForm, getFormValues } from 'redux-form';
+import { validate } from './validation';
+import { CUSTOMER_FORM, CUSTOMER_ADD } from '../../constants';
+import * as customerAction from '../../actions'
+import { Customer } from '../../components/Customer';
+
+
+const mapStateToProps = (state, { navigation }) => {
+ const {
+ global: { language, currencies },
+ customers: {
+ countries,
+ loading: {
+ customerLoading,
+ getEditCustomerLoading,
+ countriesLoading
+ }
+ }
+ } = state
+ let customerId = navigation.getParam('customerId', null);
+ let type = navigation.getParam('type', CUSTOMER_ADD)
+
+ return {
+ formValues: getFormValues(CUSTOMER_FORM)(state) || {},
+ type,
+ language,
+ currencies,
+ countries,
+ customerLoading,
+ getEditCustomerLoading,
+ countriesLoading,
+
+ initialValues: {
+ enable_portal: false,
+ currency_id: null,
+ id: customerId,
+ }
+ };
+};
+
+const mapDispatchToProps = {
+ createCustomer: customerAction.createCustomer,
+ editCustomer: customerAction.editCustomer,
+ getEditCustomer: customerAction.getEditCustomer,
+ removeCustomer: customerAction.removeCustomer,
+ getCountries: customerAction.getCountries,
+};
+
+// Redux Forms
+const addEditCustomerReduxForm = reduxForm({
+ form: CUSTOMER_FORM,
+ validate,
+})(Customer);
+
+// connect
+const AddEditCustomerContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(addEditCustomerReduxForm);
+
+AddEditCustomerContainer.navigationOptions = () => ({
+ header: null,
+});
+
+
+export default AddEditCustomerContainer;
diff --git a/src/features/customers/containers/Customer/validation.js b/src/features/customers/containers/Customer/validation.js
new file mode 100644
index 00000000..d62c329f
--- /dev/null
+++ b/src/features/customers/containers/Customer/validation.js
@@ -0,0 +1,39 @@
+
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const {
+ name,
+ enable_portal,
+ password,
+ id,
+ email,
+ website,
+ phone
+ } = values;
+
+ errors.name = getError(name, ['required']);
+
+ if (email)
+ errors.email = getError(email, ['emailFormat']);
+
+ if (website)
+ errors.website = getError(website, ['urlFormat']);
+
+ if (phone)
+ errors.phone = getError(phone, ['isNumberFormat']);
+
+ /* if (enable_portal && !id) {
+ errors.password = getError(
+ password,
+ ['requiredField'],
+ { fieldName: 'Password' },
+ );
+ } */
+
+ return errors;
+};
diff --git a/src/features/customers/containers/Customers/index.js b/src/features/customers/containers/Customers/index.js
new file mode 100644
index 00000000..f09c59d3
--- /dev/null
+++ b/src/features/customers/containers/Customers/index.js
@@ -0,0 +1,71 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Customers } from '../../components/Customers';
+import * as CustomersAction from '../../actions';
+import { reduxForm, getFormValues } from 'redux-form';
+import { CUSTOMER_SEARCH } from '../../constants';
+import { colors } from '../../../../styles/colors';
+import { SvgXml } from 'react-native-svg';
+import { CUSTOMERS } from '../../../../assets/svg';
+import { getTitleByLanguage, tabBarOnPress, navigateRoute, navigateTabRoutes } from '../../../../navigation/actions';
+import { ROUTES } from '../../../../navigation/routes';
+
+const mapStateToProps = (state) => {
+
+ const {
+ customers: { customers, filterCustomers, loading },
+ global: { language }
+ } = state;
+
+ return {
+ customers,
+ filterCustomers,
+ loading: loading.customersLoading,
+ language,
+ formValues: getFormValues(CUSTOMER_SEARCH)(state) || {},
+ };
+};
+
+const mapDispatchToProps = {
+ getCustomer: CustomersAction.getCustomers
+};
+
+// Redux Forms
+const customerSearchReduxForm = reduxForm({
+ form: CUSTOMER_SEARCH,
+})(Customers);
+
+// connect
+const CustomersContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(customerSearchReduxForm);
+
+
+CustomersContainer.navigationOptions = ({ navigation }) => ({
+ gesturesEnabled: false,
+ tabBarLabel: getTitleByLanguage('tabNavigation.customers'),
+ tabBarIcon: ({ focused }: { focused: boolean }) => (
+
+ ),
+ tabBarOnPress: () => {
+
+ navigateTabRoutes(ROUTES.MAIN_CUSTOMERS, { apiCall: false })
+
+ let apiCall = navigation.getParam('apiCall', false)
+
+ apiCall ? navigateRoute(ROUTES.MAIN_CUSTOMERS) : tabBarOnPress(
+ ROUTES.MAIN_CUSTOMERS,
+ CustomersAction.getCustomers,
+ navigation
+ )
+
+ }
+});
+
+export default CustomersContainer;
diff --git a/src/features/customers/reducers/index.js b/src/features/customers/reducers/index.js
new file mode 100644
index 00000000..190fd0fb
--- /dev/null
+++ b/src/features/customers/reducers/index.js
@@ -0,0 +1,124 @@
+import {
+ CUSTOMERS_TRIGGER_SPINNER,
+ SET_CUSTOMERS,
+ SET_COUNTRIES,
+ SET_STATES,
+ SET_CITIES,
+ SET_CREATE_CUSTOMER,
+ SET_EDIT_CUSTOMER,
+ SET_REMOVE_CUSTOMER,
+ SET_FILTER_CUSTOMERS
+} from "../constants";
+
+const formatCustomers = (customers) => {
+
+ let customerList = []
+ if (typeof customers !== 'undefined' && customers.length != 0) {
+ customerList = customers.map((customer) => {
+
+ return {
+ title: customer.name,
+ subtitle: {
+ title: customer.contact_name || '',
+ },
+ leftAvatar: customer.name.toUpperCase().charAt(0),
+ fullItem: customer,
+ }
+ })
+ }
+ return customerList
+}
+
+const initialState = {
+ customers: [],
+ filterCustomers: [],
+ countries: [],
+ states: [],
+ cities: [],
+ errors: null,
+ loading: {
+ customersLoading: false,
+ customerLoading: false,
+ getEditCustomerLoading: false,
+ countriesLoading: false,
+ statesLoading: false,
+ citiesLoading: false
+ }
+};
+
+export default function customersReducer(state = initialState, action) {
+ const { payload, type } = action;
+
+ switch (type) {
+
+
+ case SET_CUSTOMERS:
+
+ let { customers, fresh } = payload;
+ let customerList = formatCustomers(customers)
+ if (!fresh) {
+ return {
+ ...state,
+ customers: [...state.customers, ...customerList]
+ };
+ }
+
+ return { ...state, customers: customerList };
+
+ case SET_FILTER_CUSTOMERS:
+
+ let filterCustomerList = formatCustomers(payload.customers)
+
+ if (!payload.fresh) {
+ return {
+ ...state,
+ filterCustomers: [...state.filterCustomers, ...filterCustomerList]
+ };
+ }
+
+ return { ...state, filterCustomers: filterCustomerList };
+
+ case SET_EDIT_CUSTOMER:
+
+ let editCustomer = formatCustomers(payload.customers)
+
+ const customersList = state.customers.filter(({ fullItem }) =>
+ (fullItem.id !== payload.id))
+
+ return {
+ ...state,
+ customers: [...editCustomer, ...customersList]
+ };
+
+ case SET_CREATE_CUSTOMER:
+
+ let newCustomer = formatCustomers(payload.customers)
+
+ return {
+ ...state,
+ customers: [...newCustomer, ...state.customers]
+ };
+
+ case SET_REMOVE_CUSTOMER:
+
+ const remainCustomers = state.customers.filter(({ fullItem }) =>
+ (fullItem.id !== payload.id))
+
+ return { ...state, customers: remainCustomers };
+
+ case SET_COUNTRIES:
+ return { ...state, ...payload };
+
+ case SET_STATES:
+ return { ...state, ...payload };
+
+ case SET_CITIES:
+ return { ...state, ...payload };
+
+ case CUSTOMERS_TRIGGER_SPINNER:
+ return { ...state, loading: { ...payload } };
+
+ default:
+ return state;
+ }
+}
diff --git a/src/features/customers/saga/index.js b/src/features/customers/saga/index.js
new file mode 100644
index 00000000..9bd89b13
--- /dev/null
+++ b/src/features/customers/saga/index.js
@@ -0,0 +1,368 @@
+import { call, put, takeEvery, takeLatest } from 'redux-saga/effects';
+
+import Request from '../../../api/request';
+import {
+ GET_CUSTOMERS,
+ GET_COUNTRIES,
+ GET_STATES,
+ GET_CITIES,
+ CREATE_CUSTOMER,
+ EDIT_CUSTOMER,
+ GET_EDIT_CUSTOMER,
+ REMOVE_CUSTOMER,
+ // Endpoint Api URL
+ GET_CUSTOMERS_URL,
+ GET_COUNTRIES_URL,
+ GET_STATES_URL,
+ GET_CITIES_URL,
+ CREATE_CUSTOMER_URL,
+ EDIT_CUSTOMER_URL,
+ GET_EDIT_CUSTOMER_URL,
+ REMOVE_CUSTOMER_URL,
+} from '../constants';
+
+import {
+ customerTriggerSpinner,
+ setCustomers,
+ setCountries,
+ setStates,
+ setCities,
+ setCreateCustomer,
+ setEditCustomer,
+ setRemoveCustomer,
+ setFilterCustomers,
+} from '../actions';
+import { ROUTES } from '../../../navigation/routes';
+import { alertMe } from '../../../api/global';
+import { getTitleByLanguage } from '../../../navigation/actions';
+
+
+const addressParams = (address, type) => {
+ let params = {
+ name: address.name ? address.name : null,
+ address_street_1: address.address_street_1 ? address.address_street_1 : null,
+ address_street_2: address.address_street_2 ? address.address_street_2 : null,
+ city_id: address.city_id ? address.city_id : null,
+ state_id: address.state_id ? address.state_id : null,
+ country_id: address.country_id ? address.country_id : null,
+ zip: address.zip ? address.zip : null,
+ phone: address.phone ? address.phone : null,
+ type: type,
+ }
+ return params
+}
+
+function* getCustomers(payloadData) {
+ const {
+ payload: {
+ onResult = null,
+ fresh = true,
+ onMeta = null,
+ params = null,
+ filter = false,
+ pagination: { page = 1, limit = 10 } = {},
+ } = {},
+ } = payloadData;
+
+
+ yield put(customerTriggerSpinner({ customersLoading: true }));
+
+ try {
+
+ let param = {
+ ...params,
+ page,
+ limit
+ }
+ const options = {
+ path: GET_CUSTOMERS_URL(param),
+ };
+ const response = yield call([Request, 'get'], options);
+
+ if (!filter)
+ yield put(setCustomers({ customers: response.customers.data, fresh }));
+
+ else
+ yield put(setFilterCustomers({ customers: response.customers.data, fresh }));
+
+ onMeta && onMeta(response.customers);
+
+ onResult && onResult(true);
+ } catch (error) {
+ onResult && onResult(false);
+ } finally {
+ yield put(customerTriggerSpinner({ customersLoading: false }));
+ }
+}
+
+function* getCountries(payloadData) {
+ const {
+ payload: {
+ onResult,
+ countryId
+ },
+ } = payloadData;
+
+ yield put(customerTriggerSpinner({ countriesLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_COUNTRIES_URL(),
+ };
+
+ const response = yield call([Request, 'get'], options);
+ onResult && onResult(response.countries[100]);
+ yield put(setCountries({ countries: response.countries }));
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(customerTriggerSpinner({ countriesLoading: false }));
+ }
+}
+
+function* getStates(payloadData) {
+ const {
+ payload: { countryId, onResult },
+ } = payloadData;
+
+ yield put(customerTriggerSpinner({ statesLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_STATES_URL(countryId),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ onResult && onResult(response.states[0]);
+ yield put(setStates({ states: response.states }));
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(customerTriggerSpinner({ statesLoading: false }));
+ }
+}
+
+function* getCities(payloadData) {
+ const {
+ payload: { stateID, onResult },
+ } = payloadData;
+
+ yield put(customerTriggerSpinner({ citiesLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_CITIES_URL(stateID),
+ };
+
+ const response = yield call([Request, 'get'], options);
+ onResult && onResult(response.cities[0]);
+ yield put(setCities({ cities: response.cities }));
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(customerTriggerSpinner({ citiesLoading: false }));
+ }
+}
+
+function* createCustomer(payloadData) {
+ const {
+ payload: {
+ params: {
+ website,
+ phone,
+ name,
+ email,
+ enable_portal,
+ billingAddress,
+ shippingAddress,
+ contact_name,
+ currency_id,
+ password,
+ },
+ onResult,
+ navigation
+ },
+ } = payloadData;
+
+ let addresses = []
+
+ if (typeof billingAddress !== 'undefined' && (billingAddress && Object.keys(billingAddress).length !== 0)) {
+ let billing = addressParams(billingAddress, "BILLING")
+ addresses.push(billing)
+ }
+ if (typeof shippingAddress !== 'undefined' && (shippingAddress && Object.keys(shippingAddress).length !== 0)) {
+ let shipping = addressParams(shippingAddress, "SHIPPING")
+ addresses.push(shipping)
+ }
+
+ yield put(customerTriggerSpinner({ customerLoading: true }));
+
+ try {
+
+ const options = {
+ path: CREATE_CUSTOMER_URL(),
+ body: {
+ name,
+ currency_id,
+ email,
+ phone,
+ contact_name,
+ website,
+ enable_portal,
+ password,
+ addresses
+ }
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ if (response.error) {
+ alertMe({ desc: getTitleByLanguage('customers.alertEmailAlreadyInUse') })
+ }
+
+ yield put(setCreateCustomer({ customers: [response.customer] }));
+ onResult && onResult(response.customer)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(customerTriggerSpinner({ customerLoading: false }));
+ }
+}
+
+function* editCustomer(payloadData) {
+ const {
+ payload: {
+ params: {
+ website,
+ phone,
+ name,
+ email,
+ enable_portal,
+ password,
+ billingAddress,
+ shippingAddress,
+ contact_name,
+ currency_id,
+ id
+ },
+ navigation
+ },
+ } = payloadData;
+
+ let addresses = []
+
+ if (typeof billingAddress !== 'undefined' && (billingAddress && Object.keys(billingAddress).length !== 0)) {
+ let billing = addressParams(billingAddress, "BILLING")
+ addresses.push(billing)
+ }
+ if (typeof shippingAddress !== 'undefined' && (shippingAddress && Object.keys(shippingAddress).length !== 0)) {
+ let shipping = addressParams(shippingAddress, "SHIPPING")
+ addresses.push(shipping)
+ }
+
+ yield put(customerTriggerSpinner({ customerLoading: true }));
+
+ try {
+
+ const options = {
+ path: EDIT_CUSTOMER_URL(id),
+ body: {
+ name,
+ currency_id,
+ email,
+ phone,
+ contact_name,
+ website,
+ enable_portal,
+ password,
+ addresses
+ }
+ };
+
+ const response = yield call([Request, 'put'], options);
+
+ if (response.error) {
+ alertMe({ desc: getTitleByLanguage('customers.alertEmailAlreadyInUse') })
+ }
+
+ if (response.success) {
+ navigation.navigate(ROUTES.MAIN_CUSTOMERS)
+ yield put(setEditCustomer({ customers: [response.customer], id: id }));
+ }
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(customerTriggerSpinner({ customerLoading: false }));
+ }
+}
+
+
+
+function* getEditCustomer(payloadData) {
+ const {
+ payload: { id, onResult },
+ } = payloadData;
+
+ yield put(customerTriggerSpinner({ getEditCustomerLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_EDIT_CUSTOMER_URL(id),
+ };
+
+ const response = yield call([Request, 'get'], options);
+ onResult && onResult(response.customer)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(customerTriggerSpinner({ getEditCustomerLoading: false }));
+ }
+}
+
+function* removeCustomer(payloadData) {
+ const {
+ payload: { id, navigation },
+ } = payloadData;
+
+ yield put(customerTriggerSpinner({ customerLoading: true }));
+
+ try {
+
+ const options = {
+ path: REMOVE_CUSTOMER_URL(id),
+ };
+
+ const response = yield call([Request, 'delete'], options);
+
+
+ navigation.navigate(ROUTES.MAIN_CUSTOMERS)
+ if (response.success)
+ yield put(setRemoveCustomer({ id }));
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(customerTriggerSpinner({ customerLoading: false }));
+ }
+}
+
+export default function* customersSaga() {
+ yield takeEvery(GET_CUSTOMERS, getCustomers);
+ yield takeEvery(GET_COUNTRIES, getCountries);
+ yield takeEvery(GET_STATES, getStates);
+ yield takeEvery(GET_CITIES, getCities);
+ yield takeEvery(CREATE_CUSTOMER, createCustomer);
+ yield takeEvery(EDIT_CUSTOMER, editCustomer);
+ yield takeEvery(GET_EDIT_CUSTOMER, getEditCustomer);
+ yield takeEvery(REMOVE_CUSTOMER, removeCustomer);
+}
diff --git a/src/features/estimates/actions/index.js b/src/features/estimates/actions/index.js
new file mode 100644
index 00000000..8b001c22
--- /dev/null
+++ b/src/features/estimates/actions/index.js
@@ -0,0 +1,148 @@
+import {
+ GET_ESTIMATES,
+ SET_ESTIMATES,
+ CLEAR_ESTIMATES,
+ GET_CREATE_ESTIMATE,
+ SET_CREATE_ESTIMATE,
+ ESTIMATES_TRIGGER_SPINNER,
+ ADD_ITEM,
+ GET_ITEMS,
+ SET_ITEMS,
+ SET_ESTIMATE_ITEMS,
+ CREATE_ESTIMATE,
+ EDIT_ITEM,
+ SET_EDIT_ESTIMATE_ITEMS,
+ REMOVE_ITEM,
+ REMOVE_ESTIMATE_ITEM,
+ GET_EDIT_ESTIMATE,
+ SET_EDIT_ESTIMATE,
+ EDIT_ESTIMATE,
+ REMOVE_ESTIMATE_ITEMS,
+ CLEAR_ESTIMATE,
+ SET_ESTIMATE,
+ CONVERT_TO_INVOICE,
+ REMOVE_ESTIMATE,
+ REMOVE_FROM_ESTIMATES,
+ CHANGE_ESTIMATE_STATUS,
+} from "../constants";
+
+export const getEstimates = (payload = {}) => ({
+ type: GET_ESTIMATES,
+ payload,
+});
+
+export const setEstimates = (payload = {}) => ({
+ type: SET_ESTIMATES,
+ payload,
+});
+
+export const clearEstimates = (payload = {}) => ({
+ type: CLEAR_ESTIMATES,
+ payload,
+});
+
+export const clearEstimate = (payload = {}) => ({
+ type: CLEAR_ESTIMATE,
+ payload,
+});
+
+export const getCreateEstimate = (payload = {}) => ({
+ type: GET_CREATE_ESTIMATE,
+ payload,
+});
+
+export const getEditEstimate = (payload = {}) => ({
+ type: GET_EDIT_ESTIMATE,
+ payload,
+});
+
+export const createEstimate = (payload = {}) => ({
+ type: CREATE_ESTIMATE,
+ payload,
+});
+
+export const editEstimate = (payload = {}) => ({
+ type: EDIT_ESTIMATE,
+ payload,
+});
+
+export const setEstimate = (payload = {}) => ({
+ type: SET_ESTIMATE,
+ payload,
+});
+
+export const setEditEstimate = (payload = {}) => ({
+ type: SET_EDIT_ESTIMATE,
+ payload,
+});
+
+export const estimateTriggerSpinner = (payload) => ({
+ type: ESTIMATES_TRIGGER_SPINNER,
+ payload,
+});
+
+export const addItem = (payload = {}) => ({
+ type: ADD_ITEM,
+ payload,
+});
+
+export const getItems = (payload = {}) => ({
+ type: GET_ITEMS,
+ payload,
+});
+
+export const setItems = (payload = {}) => ({
+ type: SET_ITEMS,
+ payload,
+});
+
+export const setEstimateItems = (payload = {}) => ({
+ type: SET_ESTIMATE_ITEMS,
+ payload,
+});
+
+export const editItem = (payload = {}) => ({
+ type: EDIT_ITEM,
+ payload,
+});
+
+export const setEditEstimateItem = (payload = {}) => ({
+ type: SET_EDIT_ESTIMATE_ITEMS,
+ payload,
+});
+
+export const removeItem = (payload = {}) => ({
+ type: REMOVE_ITEM,
+ payload,
+});
+
+export const removeEstimateItem = (payload = {}) => ({
+ type: REMOVE_ESTIMATE_ITEM,
+ payload,
+});
+
+export const removeEstimateItems = () => ({
+ type: REMOVE_ESTIMATE_ITEMS
+});
+
+
+export const convertToInvoice = (payload = {}) => ({
+ type: CONVERT_TO_INVOICE,
+ payload,
+});
+
+export const removeEstimate = (payload = {}) => ({
+ type: REMOVE_ESTIMATE,
+ payload,
+});
+
+export const removeFromEstimates = (payload = {}) => ({
+ type: REMOVE_FROM_ESTIMATES,
+ payload,
+});
+
+export const changeEstimateStatus = (payload = {}) => ({
+ type: CHANGE_ESTIMATE_STATUS,
+ payload,
+});
+
diff --git a/src/features/estimates/components/Estimate/index.js b/src/features/estimates/components/Estimate/index.js
new file mode 100644
index 00000000..c3957032
--- /dev/null
+++ b/src/features/estimates/components/Estimate/index.js
@@ -0,0 +1,1033 @@
+// @flow
+
+import React from 'react';
+import {
+ View,
+ Text,
+ Alert,
+ Linking
+} from 'react-native';
+import { change } from 'redux-form';
+import { Field } from 'redux-form';
+import styles from './styles';
+import {
+ InputField,
+ DatePickerField,
+ CtDivider,
+ CtButton,
+ ListView,
+ DefaultLayout,
+ SelectField,
+ SelectPickerField,
+ CurrencyFormat,
+} from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import {
+ ESTIMATE_ADD,
+ ESTIMATE_EDIT,
+ ITEM_ADD,
+ ITEM_EDIT,
+ ESTIMATE_FORM,
+ ADD_ESTIMATE_ACTIONS,
+ ESTIMATE_ACTIONS,
+ EDIT_ESTIMATE_ACTIONS,
+ MARK_AS_ACCEPT, MARK_AS_REJECT, MARK_AS_SENT
+} from '../../constants';
+import { BUTTON_TYPE } from '../../../../api/consts/core';
+import { colors } from '../../../../styles/colors';
+import { TemplateField } from '../TemplateField';
+import { MOUNT, UNMOUNT, goBackWithFunction } from '../../../../navigation/actions';
+import Lng from '../../../../api/lang/i18n';
+import { ESTIMATE_DISCOUNT_OPTION } from '../../constants';
+import { CUSTOMER_ADD } from '../../../customers/constants';
+import { IMAGES } from '../../../../config';
+import { ADD_TAX } from '../../../settings/constants';
+import { MAX_LENGTH } from '../../../../api/global';
+import { itemsDescriptionStyle } from '../../../invoices/components/Invoice/styles';
+import { headerTitle } from '../../../../api/helper';
+
+type IProps = {
+ navigation: Object,
+ estimateItems: Object,
+ taxTypes: Object,
+ customers: Object,
+ getCreateEstimate: Function,
+ getEditEstimate: Function,
+ clearEstimate: Function,
+ createEstimate: Function,
+ handleSubmit: Function,
+ getCustomers: Function,
+ getItems: Function,
+ editEstimate: Boolean,
+ itemsLoading: Boolean,
+ initLoading: Boolean,
+ loading: Boolean,
+ estimateData: Object,
+ estimateItems: Object,
+ items: Object,
+ language: String,
+ type: String
+
+}
+export class Estimate extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ taxTypeList: [],
+ currency: {},
+ itemList: [],
+ customerName: '',
+ markAsStatus: null,
+ };
+ }
+
+ componentDidMount() {
+
+ const {
+ getCreateEstimate,
+ navigation,
+ estimateItems,
+ taxTypes,
+ getEditEstimate,
+ type,
+ } = this.props;
+
+ type === ESTIMATE_EDIT ?
+ getEditEstimate({
+ id: navigation.getParam('id'),
+ onResult: ({ user: { currency, name }, status }) => {
+
+ this.setState({
+ currency,
+ taxTypeList: taxTypes,
+ customerName: name,
+ markAsStatus: status
+ })
+ }
+ }) :
+ getCreateEstimate({
+ onResult: (val) => {
+ const { currency } = val
+
+ this.setState({ taxTypeList: taxTypes, currency })
+ }
+ });
+
+ navigation.addListener(
+ 'didFocus',
+ payload => {
+ this.forceUpdate();
+ }
+ );
+
+ this.getEstimateItemList(estimateItems)
+
+ }
+
+
+ componentWillUnmount() {
+ const { clearEstimate } = this.props
+ clearEstimate();
+ goBackWithFunction(UNMOUNT)
+ }
+
+ androidBackHandler = () => {
+ const { navigation, handleSubmit } = this.props
+ goBackWithFunction(MOUNT, navigation, () => this.onDraft(handleSubmit))
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(ESTIMATE_FORM, field, value));
+ };
+
+ onEditItem = (item) => {
+ const {
+ navigation,
+ estimateData: { discount_per_item, tax_per_item }
+ } = this.props
+ const { currency } = this.state
+
+ navigation.navigate(
+ ROUTES.ESTIMATE_ITEM,
+ { item, type: ITEM_EDIT, currency, discount_per_item, tax_per_item }
+ )
+ }
+
+ onDraft = (handleSubmit) => {
+ const { language, navigation, type } = this.props
+ if (type === ESTIMATE_EDIT) {
+ navigation.navigate(ROUTES.ESTIMATE_LIST)
+ return
+ }
+ Alert.alert(
+ Lng.t("estimates.alert.draftTitle", { locale: language }),
+ '',
+ [
+ {
+ text: Lng.t("alert.action.saveAsDraft", { locale: language }),
+ onPress: handleSubmit(this.onSubmitEstimate)
+ },
+ {
+ text: Lng.t("alert.action.discard", { locale: language }),
+ onPress: () => {
+ navigation.navigate(ROUTES.ESTIMATE_LIST)
+ },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ }
+
+ onSubmitEstimate = (values, status = 'draft') => {
+
+ const {
+ createEstimate,
+ navigation,
+ type,
+ editEstimate,
+ language,
+ } = this.props
+
+ if (this.finalAmount() < 0) {
+ alert(Lng.t("estimates.alert.lessAmount", { locale: language }))
+ return
+ }
+
+ let estimate = {
+ ...values,
+ total: this.finalAmount(),
+ sub_total: this.estimateSubTotal(),
+ tax: this.estimateTax() + this.estimateCompoundTax(),
+ discount_val: this.totalDiscount(),
+ taxes: values.taxes ? values.taxes.map(val => {
+ return {
+ ...val,
+ amount: val.compound_tax ?
+ this.getCompoundTaxValue(val.percent) :
+ this.getTaxValue(val.percent),
+ }
+ }) : [],
+ }
+
+ if (status === 'send') {
+ estimate.estimateSend = true
+ }
+
+ type === ESTIMATE_ADD ?
+ createEstimate({
+ estimate,
+ onResult: (url) => {
+ if (status === 'download') {
+ Linking.openURL(url);
+ }
+ navigation.navigate(ROUTES.ESTIMATE_LIST)
+ }
+ }) :
+ editEstimate({
+ estimate: { ...estimate, id: navigation.getParam('id') },
+ onResult: (url) => {
+ if (status === 'download') {
+ Linking.openURL(url);
+ }
+ navigation.navigate(ROUTES.ESTIMATE_LIST)
+ }
+ })
+ };
+
+ estimateSubTotal = () => {
+ const { estimateItems } = this.props
+ let subTotal = 0
+ estimateItems.map(val => {
+ subTotal += JSON.parse(val.total)
+ })
+
+ return JSON.parse(subTotal)
+ }
+
+ subTotal = () => {
+ let estimateTax = 0
+ this.estimateItemTotalTaxes().filter(val => {
+ estimateTax += val.amount
+ })
+ return (this.estimateSubTotal() + estimateTax) - this.totalDiscount()
+ }
+
+ estimateTax = () => {
+ const { formValues: { taxes } } = this.props
+
+ let totalTax = 0
+
+ taxes && taxes.map(val => {
+ if (!val.compound_tax) {
+ totalTax += this.getTaxValue(val.percent)
+ }
+ })
+
+ return totalTax
+ }
+
+ estimateCompoundTax = () => {
+ const { formValues: { taxes } } = this.props
+
+ let totalTax = 0
+
+ taxes && taxes.map(val => {
+ if (val.compound_tax) {
+ totalTax += this.getCompoundTaxValue(val.percent)
+ }
+ })
+
+ return totalTax
+ }
+
+ getTaxValue = (tax) => {
+ return (tax * JSON.parse(this.subTotal())) / 100
+ }
+
+ getCompoundTaxValue = (tax) => {
+ return (tax * JSON.parse(this.totalAmount())) / 100
+ }
+
+ getTaxName = (tax) => {
+ const { taxTypes } = this.props
+ let taxName = ''
+ const type = taxTypes && taxTypes.filter(val => val.fullItem.id === tax.tax_type_id)
+
+ if (type.length > 0) {
+ taxName = type[0]['fullItem'].name
+ }
+ return taxName
+ }
+
+ totalDiscount = () => {
+ const { formValues: { discount, discount_type } } = this.props
+
+ let discountPrice = 0
+
+ if (discount_type === 'percentage') {
+ discountPrice = ((discount * this.estimateSubTotal()) / 100)
+ } else {
+ discountPrice = (discount * 100)
+ }
+
+ return discountPrice
+ }
+
+ totalAmount = () => {
+ return this.subTotal() + this.estimateTax()
+ }
+
+ finalAmount = () => {
+ return this.totalAmount() + this.estimateCompoundTax()
+ }
+
+ estimateItemTotalTaxes = () => {
+ const { estimateItems } = this.props
+ let taxes = []
+ estimateItems.map(val => {
+ val.taxes && val.taxes.filter(tax => {
+ let hasSame = false
+ const { tax_type_id, id, amount } = tax
+
+ taxes = taxes.map(tax2 => {
+ if ((tax_type_id || id) === tax2.tax_type_id) {
+ hasSame = true
+ return {
+ ...tax2,
+ amount: amount + tax2.amount,
+ tax_type_id: tax2.tax_type_id
+ }
+ }
+ return tax2
+ })
+
+ if (!hasSame) {
+ taxes.push({ ...tax, tax_type_id: (tax_type_id || id) })
+ }
+ })
+ })
+ return taxes
+ }
+
+ FINAL_AMOUNT = () => {
+ const { currency } = this.state
+
+ const {
+ language,
+ taxTypes,
+ navigation,
+ estimateData: { discount_per_item, tax_per_item },
+ formValues: { taxes }
+ } = this.props
+
+ let taxPerItem = !(tax_per_item === 'NO' || typeof tax_per_item === 'undefined' || tax_per_item === null)
+
+ let discountPerItem = !(discount_per_item === 'NO' || typeof discount_per_item === 'undefined' || discount_per_item === null)
+
+ return (
+
+
+
+
+ {Lng.t("estimates.subtotal", { locale: language })}
+
+
+
+
+
+
+
+ {(!discountPerItem) && (
+
+
+
+ {Lng.t("estimates.discount", { locale: language })}
+
+
+
+
+
+ {
+ this.setFormField('discount_type', val)
+ }}
+ isFakeInput
+ defaultPickerOptions={{
+ label: 'Fixed',
+ value: 'fixed',
+ color: colors.secondary,
+ displayLabel: currency ? currency.symbol : '$',
+ }}
+ fakeInputValueStyle={styles.fakeInputValueStyle}
+ fakeInputContainerStyle={styles.selectPickerField}
+ containerStyle={styles.SelectPickerContainer}
+ />
+
+
+ )}
+
+ {taxes &&
+ taxes.map((val, index) => !val.compound_tax ? (
+
+
+
+ {this.getTaxName(val)} ({val.percent} %)
+
+
+
+
+
+
+ ) : null
+ )
+ }
+
+ {taxes &&
+ taxes.map((val, index) => val.compound_tax ? (
+
+
+
+ {this.getTaxName(val)} ({val.percent} %)
+
+
+
+
+
+
+ ) : null
+ )
+ }
+
+ {this.DISPLAY_ITEM_TAX()}
+
+ {(!taxPerItem) && (
+
+ {Lng.t("estimates.taxPlaceholder", { locale: language })}
+
+ )
+ }}
+ navigation={navigation}
+ isMultiSelect
+ isInternalSearch
+ language={language}
+ concurrentMultiSelect
+ compareField="id"
+ valueCompareField="tax_type_id"
+ headerProps={{
+ title: Lng.t("taxes.title", { locale: language })
+ }}
+ rightIconPress={
+ () => navigation.navigate(ROUTES.TAX, {
+ type: ADD_TAX,
+ onSelect: (val) => {
+ this.setFormField('taxes',
+ [...val, ...taxes]
+ )
+ }
+ })
+ }
+ listViewProps={{
+ contentContainerStyle: { flex: 2 }
+ }}
+ emptyContentProps={{
+ contentType: "taxes",
+ }}
+ />
+ )}
+
+
+
+
+
+
+ {Lng.t("estimates.totalAmount", { locale: language })}:
+
+
+
+
+
+
+
+ )
+ };
+
+ BOTTOM_ACTION = () => {
+ const { language, loading, handleSubmit } = this.props
+
+ return (
+
+ this.onOptionSelect(ESTIMATE_ACTIONS.VIEW)}
+ btnTitle={Lng.t("button.viewPdf", { locale: language })}
+ type={BUTTON_TYPE.OUTLINE}
+ containerStyle={styles.handleBtn}
+ buttonContainerStyle={styles.buttonContainer}
+ loading={loading}
+ />
+
+ this.onSubmitEstimate(val, status = 'save'))}
+ btnTitle={Lng.t("button.save", { locale: language })}
+ containerStyle={styles.handleBtn}
+ buttonContainerStyle={styles.buttonContainer}
+ loading={loading}
+ />
+
+
+ )
+ }
+
+ DISPLAY_ITEM_TAX = () => {
+ const { currency } = this.state
+
+ let taxes = this.estimateItemTotalTaxes()
+
+ return taxes ? (
+ taxes.map((val, index) => (
+
+
+
+ {this.getTaxName(val)} ({val.percent} %)
+
+
+
+
+
+
+ )
+ )
+ ) : null
+ }
+
+ getEstimateItemList = (estimateItems) => {
+ this.setFormField('items', estimateItems)
+
+ const { currency } = this.state
+
+ let estimateItemList = []
+
+ if (typeof estimateItems !== 'undefined' && estimateItems.length != 0) {
+
+ estimateItemList = estimateItems.map((item) => {
+
+ let { name, description, price, quantity, total } = item
+
+ return {
+ title: name,
+ subtitle: {
+ title: description,
+ labelComponent: (
+
+ ),
+ },
+ amount: total,
+ currency,
+ fullItem: item,
+ };
+ });
+ }
+
+ return estimateItemList
+ }
+
+ getItemList = (items) => {
+ const { currency } = this.state
+
+ let itemList = []
+
+ if (typeof items !== 'undefined' && items.length != 0) {
+
+ itemList = items.map((item) => {
+
+ let { name, description, price } = item
+
+ return {
+ title: name,
+ subtitle: {
+ title: description,
+ },
+ amount: price,
+ currency,
+ fullItem: item,
+ };
+ });
+ }
+
+ return itemList
+ }
+
+ onOptionSelect = (action) => {
+ const {
+ removeEstimate,
+ navigation,
+ language,
+ convertToInvoice,
+ handleSubmit,
+ changeEstimateStatus,
+ type
+ } = this.props
+
+ switch (action) {
+ case ESTIMATE_ACTIONS.VIEW:
+ handleSubmit((val) => this.onSubmitEstimate(val, action))()
+ break;
+
+ case ESTIMATE_ACTIONS.SEND:
+ type === ESTIMATE_ADD ? handleSubmit((val) => this.onSubmitEstimate(val, action))() :
+ changeEstimateStatus({
+ id: navigation.getParam('id'),
+ action: 'send',
+ navigation
+ })
+
+ break;
+
+ case ESTIMATE_ACTIONS.MARK_AS_SENT:
+ changeEstimateStatus && changeEstimateStatus({ id: navigation.getParam('id'), action: 'mark-as-sent', navigation })
+ break;
+
+ case ESTIMATE_ACTIONS.MARK_AS_ACCEPTED:
+ changeEstimateStatus && changeEstimateStatus({ id: navigation.getParam('id'), action: 'accept', navigation })
+ break;
+
+ case ESTIMATE_ACTIONS.MARK_AS_REJECTED:
+ changeEstimateStatus && changeEstimateStatus({ id: navigation.getParam('id'), action: 'reject', navigation })
+ break;
+
+ case ESTIMATE_ACTIONS.CONVERT_TO_INVOICE:
+ Alert.alert(
+ '',
+ Lng.t("estimates.alert.convertToInvoiceDescription", { locale: language }),
+ [
+ {
+ text: 'OK',
+ onPress: () => {
+ convertToInvoice({
+ id: navigation.getParam('id'),
+ onResult: () => {
+ navigation.navigate(ROUTES.MAIN_INVOICES)
+ }
+ })
+ }
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ break;
+
+ case ESTIMATE_ACTIONS.DELETE:
+ Alert.alert(
+ Lng.t("alert.title", { locale: language }),
+ Lng.t("estimates.alert.removeDescription", { locale: language }),
+ [
+ {
+ text: 'OK',
+ onPress: () => {
+ removeEstimate({
+ id: navigation.getParam('id'),
+ onResult: () => {
+ navigation.navigate(ROUTES.ESTIMATE_LIST)
+ }
+ })
+ }
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ handleEstimateAction = (action, title) => {
+ const { handleSubmit } = this.props
+
+ Alert.alert(
+ title,
+ '',
+ [
+ {
+ text: 'OK',
+ onPress: handleSubmit((val) => this.onSubmitEstimate(val, action))
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ }
+
+ render() {
+ const {
+ navigation,
+ handleSubmit,
+ loading,
+ estimateData: {
+ estimateTemplates,
+ discount_per_item,
+ tax_per_item
+ } = {},
+ estimateItems,
+ getItems,
+ itemsLoading,
+ items,
+ language,
+ initLoading,
+ type,
+ getCustomers,
+ customers,
+ } = this.props;
+
+ const { currency, customerName, markAsStatus } = this.state
+
+ const isEditEstimate = (type === ESTIMATE_EDIT)
+
+ const estimateRefs = {}
+
+ !initLoading && this.androidBackHandler()
+
+
+ let hasMark = (markAsStatus === MARK_AS_ACCEPT) || (markAsStatus === MARK_AS_REJECT) || (markAsStatus === MARK_AS_SENT)
+
+ let drownDownProps = !initLoading ? {
+ options: isEditEstimate ?
+ EDIT_ESTIMATE_ACTIONS(language, markAsStatus) :
+ ADD_ESTIMATE_ACTIONS(language),
+ onSelect: this.onOptionSelect,
+ cancelButtonIndex: isEditEstimate ?
+ hasMark ? 5 : 6
+ : 1,
+ destructiveButtonIndex: isEditEstimate ?
+ hasMark ? 4 : 5
+ : 2
+ } : null
+
+ return (
+ this.onDraft(handleSubmit),
+ title: isEditEstimate ?
+ Lng.t("header.editEstimate", { locale: language }) :
+ Lng.t("header.addEstimate", { locale: language }),
+ titleStyle: headerTitle({ marginLeft: -15, marginRight: -15 }),
+ placement: "center",
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ loadingProps={{ is: initLoading }}
+ dropdownProps={drownDownProps}
+ >
+
+
+
+
+ this.setFormField('estimate_date', val)
+ }
+ />
+
+
+
+ this.setFormField('expiry_date', val)
+ }
+ />
+
+
+
+ {
+ estimateRefs.number = ref;
+ }}
+ editable={false}
+ />
+
+ {
+ this.setFormField('user_id', item.id)
+ this.setState({ currency: item.currency })
+ }}
+ rightIconPress={
+ () => navigation.navigate(ROUTES.CUSTOMER, {
+ type: CUSTOMER_ADD,
+ currency,
+ onSelect: (val) => {
+ this.setFormField('user_id', val.id)
+ this.setState({ currency: val.currency })
+ }
+ })
+ }
+ headerProps={{
+ title: Lng.t("customers.title", { locale: language }),
+ }}
+ listViewProps={{
+ hasAvatar: true,
+ }}
+ emptyContentProps={{
+ contentType: "customers",
+ image: IMAGES.EMPTY_CUSTOMERS,
+ }}
+ />
+
+
+ {Lng.t("estimates.items", { locale: language })}
+ *
+
+
+
+
+ {
+ navigation.navigate(ROUTES.ESTIMATE_ITEM, {
+ item,
+ currency,
+ type: ITEM_ADD,
+ discount_per_item,
+ tax_per_item
+ })
+ }
+ }
+ rightIconPress={
+ () => navigation.navigate(ROUTES.ESTIMATE_ITEM, {
+ type: ITEM_ADD,
+ currency,
+ discount_per_item,
+ tax_per_item
+ })
+ }
+ headerProps={{
+ title: Lng.t("items.title", { locale: language }),
+ }}
+ emptyContentProps={{
+ contentType: "items",
+ image: IMAGES.EMPTY_ITEMS,
+ }}
+ listViewProps={{
+ leftSubTitleStyle: itemsDescriptionStyle()
+ }}
+ />
+
+ {this.FINAL_AMOUNT(estimateItems)}
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/estimates/components/Estimate/styles.js b/src/features/estimates/components/Estimate/styles.js
new file mode 100644
index 00000000..a480db1c
--- /dev/null
+++ b/src/features/estimates/components/Estimate/styles.js
@@ -0,0 +1,175 @@
+import { StyleSheet, Platform } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+import { SymbolStyle } from '../../../../components/CurrencyFormat/styles';
+
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerTitle: {
+ fontSize: 17,
+ color: colors.dark1,
+ },
+ headerContainer: {
+ // backgroundColor: colors.veryLightGray,
+ },
+ discount: {
+ marginTop: 10,
+ },
+ required: {
+ color: colors.danger
+ },
+ selectPickerField: {
+ backgroundColor: colors.veryLightGray,
+ paddingRight: 15,
+ paddingLeft: 5,
+ ...Platform.select({
+ ios: {
+ paddingRight: 30,
+ },
+ }),
+ borderLeftWidth: 0,
+ },
+ fieldStyle: {
+ display: 'flex',
+ minWidth: 80,
+ marginTop: -6
+ },
+ discountField: {
+ display: 'flex',
+ flexDirection: 'row'
+ },
+ taxFakeInput: {
+ color: colors.primary,
+ textAlign: 'right',
+ fontFamily: fonts.poppinsMedium,
+ fontSize: 16,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ dateFieldContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ marginHorizontal: -10,
+ },
+ dateField: {
+ flex: 1,
+ paddingHorizontal: 7,
+ justifyContent: 'space-between',
+ },
+ inputFieldStyle: {
+ borderRadius: 0,
+ },
+ inputFieldContainer: {
+ paddingHorizontal: 0,
+ },
+ inputTextStyle: {
+ color: colors.dark3,
+ fontFamily: fonts.poppinsMedium,
+ },
+ inputFieldValidation: {
+ marginHorizontal: 0,
+ },
+ hintStyle: {
+ marginTop: -6,
+ paddingBottom: 6,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+ noteHintStyle: {
+ paddingBottom: 6,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+
+ amountContainer: {
+ borderWidth: 0.8,
+ borderColor: colors.lightGray,
+ marginTop: 24,
+ padding: 20,
+ marginBottom: 13,
+ backgroundColor: colors.white,
+ },
+ subContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ amountHeading: {
+ color: colors.darkGray,
+ fontFamily: fonts.poppinsMedium,
+ marginTop: 6,
+ },
+ subAmount: {
+ color: colors.dark2,
+ fontSize: 16,
+ },
+ finalAmount: {
+ color: colors.primary,
+ fontSize: 18,
+ fontWeight: '500',
+ fontFamily: fonts.poppinsMedium,
+ },
+ divider: {
+ backgroundColor: colors.lightGray,
+ borderColor: colors.lightGray,
+ borderWidth: 0.7,
+ marginTop: 10,
+ marginBottom: 8,
+ },
+ submitButton: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ },
+ handleBtn: {
+ marginHorizontal: 9,
+ },
+ buttonContainer: {
+ flex: 1,
+ },
+ itemContainer: {
+ marginVertical: 4,
+ borderWidth: 1,
+ borderColor: colors.lightGray
+ },
+ itemLeftTitle: {
+ fontSize: 15,
+ fontFamily: fonts.poppins,
+ color: colors.dark
+ },
+ itemLeftSubTitleLabel: {
+ marginLeft: -6,
+ },
+ itemLeftSubTitle: {
+ color: colors.darkGray,
+ fontSize: 13,
+ },
+ itemRightTitle: {
+ fontFamily: fonts.poppins,
+ fontSize: 18,
+ color: colors.secondary
+ },
+ label: {
+ paddingBottom: 10,
+ paddingTop: 15,
+ },
+ SelectPickerContainer: {
+ marginTop: 0
+ },
+ fakeInputValueStyle: {
+ fontSize: 18,
+ ...Platform.select({
+ android: {
+ marginTop: -4,
+ },
+ }),
+ ...SymbolStyle
+ }
+});
diff --git a/src/features/estimates/components/Estimates/index.js b/src/features/estimates/components/Estimates/index.js
new file mode 100644
index 00000000..fd0711f4
--- /dev/null
+++ b/src/features/estimates/components/Estimates/index.js
@@ -0,0 +1,451 @@
+// @flow
+
+import React from 'react';
+import { View, Text } from 'react-native';
+import { change } from 'redux-form';
+import styles from './styles';
+import { Tabs, MainLayout } from '../../../../components';
+import Sent from '../Tab/Sent';
+import Draft from '../Tab/Draft';
+import All from '../Tab/All';
+
+import { ROUTES } from '../../../../navigation/routes';
+import { ESTIMATES_TABS, ESTIMATE_ADD, ESTIMATE_EDIT, ESTIMATE_SEARCH, FILTER_ESTIMATE_STATUS, TAB_NAME } from '../../constants';
+import Lng from '../../../../api/lang/i18n';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { IMAGES } from '../../../../config';
+
+let params = {
+ search: '',
+ customer_id: '',
+ estimate_number: '',
+ from_date: '',
+ to_date: '',
+}
+
+
+type IProps = {
+ language: String,
+ navigation: Object,
+ estimates: Object,
+ customers: Object,
+ loading: Boolean,
+ handleSubmit: Function,
+ getCustomers: Function,
+}
+export class Estimates extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ activeTab: ESTIMATES_TABS.DRAFT,
+ refreshing: false,
+ fresh: true,
+ pagination: {
+ page: 1,
+ limit: 10,
+ lastPage: 1,
+ },
+ search: '',
+ filter: false,
+ selectedFromDate: '',
+ selectedToDate: '',
+ selectedFromDateValue: '',
+ selectedToDateValue: ''
+ };
+ }
+
+ componentDidMount() {
+ this.getItems({ fresh: true, q: '', type: 'DRAFT' });
+
+ const { navigation } = this.props
+ goBack(MOUNT, navigation, ROUTES.MAIN_MORE)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ setActiveTab = (activeTab) => {
+ const { refreshing, search } = this.state;
+
+ this.setState({ filter: false })
+
+ if (!refreshing) {
+ let type = this.getActiveTab(activeTab)
+
+ this.getItems({ fresh: true, type, q: search });
+
+ this.setState({ activeTab });
+ }
+ };
+
+
+ getItems = ({
+ fresh = false,
+ onResult,
+ type,
+ params,
+ q = '',
+ resetFilter = false,
+ } = {}) => {
+ const { getEstimates } = this.props;
+ const { refreshing, pagination } = this.state;
+
+ if (refreshing) {
+ return;
+ }
+
+ if (resetFilter)
+ this.setState({ filter: false })
+
+ this.setState({
+ refreshing: true,
+ fresh,
+ });
+
+ const paginationParams = fresh ? { ...pagination, page: 1 } : pagination;
+
+ if (!fresh && paginationParams.lastPage < paginationParams.page) {
+ return;
+ }
+
+ getEstimates({
+ fresh,
+ type,
+ pagination: paginationParams,
+ params: { ...params, search: q },
+ onMeta: ({ last_page, current_page }) => {
+ this.setState({
+ pagination: {
+ ...paginationParams,
+ lastPage: last_page,
+ page: current_page + 1,
+ },
+ });
+ },
+ onResult: (val) => {
+ this.setState({
+ refreshing: false,
+ fresh: !val,
+ });
+ onResult && onResult();
+ },
+ });
+ };
+
+ onEstimateSelect = (estimate) => {
+ const { navigation } = this.props
+
+ navigation.navigate(ROUTES.ESTIMATE, { id: estimate.id, type: ESTIMATE_EDIT })
+ this.onResetFilter(ESTIMATES_TABS.ALL)
+ this.setActiveTab(ESTIMATES_TABS.ALL)
+ };
+
+ onSearch = (search) => {
+ const type = this.getActiveTab()
+ this.setState({ search })
+ this.getItems({ fresh: true, q: search, type })
+ };
+
+ getActiveTab = (activeTab = this.state.activeTab) => {
+ let type = '';
+
+ if (activeTab == ESTIMATES_TABS.SENT) {
+ type = 'SENT';
+ } else if (activeTab == ESTIMATES_TABS.DRAFT) {
+ type = 'DRAFT';
+ }
+ return type
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(ESTIMATE_SEARCH, field, value));
+ };
+
+ onResetFilter = (tab = '') => {
+ const { filter } = this.state
+
+ this.setState({ filter: false })
+
+ if (filter && !tab) {
+ this.getItems({ fresh: true, q: '', type: this.getActiveTab() });
+ }
+ }
+
+ onSubmitFilter = ({ filterStatus = '', from_date = '', to_date = '', estimate_number = '', customer_id = '' }) => {
+
+ if (filterStatus || from_date || to_date || estimate_number || customer_id) {
+
+ if (filterStatus === ESTIMATES_TABS.SENT)
+ this.setState({ activeTab: ESTIMATES_TABS.SENT });
+ else if (filterStatus === ESTIMATES_TABS.DRAFT)
+ this.setState({ activeTab: ESTIMATES_TABS.DRAFT });
+ else
+ this.setState({ activeTab: ESTIMATES_TABS.ALL });
+
+ this.setState({ filter: true })
+
+ this.getItems({
+ fresh: true,
+ params: {
+ ...params,
+ customer_id,
+ estimate_number,
+ from_date,
+ to_date,
+ },
+ type: filterStatus,
+ });
+
+ }
+ else
+ this.onResetFilter()
+ }
+
+ loadMoreItems = ({ type, q }) => {
+ const { filter } = this.state
+ const {
+ formValues: {
+ filterStatus = '',
+ from_date = '',
+ to_date = '',
+ estimate_number = '',
+ customer_id = ''
+ }
+ } = this.props
+
+ if (filter) {
+
+ this.getItems({
+ params: {
+ ...params,
+ customer_id,
+ estimate_number,
+ from_date,
+ to_date,
+ },
+ type: filterStatus,
+ filter: true
+ })
+ }
+ else
+ this.getItems({ type, q });
+ }
+
+ onAddEstimate = () => {
+ const { navigation } = this.props
+ this.setActiveTab(ESTIMATES_TABS.ALL)
+ this.onResetFilter(ESTIMATES_TABS.ALL)
+ navigation.navigate(ROUTES.ESTIMATE, { type: ESTIMATE_ADD })
+ }
+
+ render() {
+ const {
+ language,
+ navigation,
+ estimates,
+ loading,
+ handleSubmit,
+ customers,
+ getCustomers,
+ } = this.props;
+
+ const {
+ activeTab,
+ refreshing,
+ pagination: { lastPage, page },
+ fresh,
+ search,
+ selectedFromDate,
+ selectedToDate,
+ selectedFromDateValue,
+ selectedToDateValue,
+ filter
+ } = this.state;
+
+ const canLoadMore = lastPage >= page;
+
+ let estimateItem = [];
+ typeof estimates !== 'undefined' && (estimateItem = estimates);
+
+ let selectFields = [
+ {
+ name: "customer_id",
+ apiSearch: true,
+ hasPagination: true,
+ getItems: getCustomers,
+ items: customers,
+ displayName: "name",
+ label: Lng.t("estimates.customer", { locale: language }),
+ icon: 'user',
+ placeholder: Lng.t("customers.placeholder", { locale: language }),
+ navigation: navigation,
+ compareField: "id",
+ onSelect: (item) => this.setFormField('customer_id', item.id),
+ headerProps: {
+ title: Lng.t("customers.title", { locale: language }),
+ rightIconPress: null
+ },
+ listViewProps: {
+ hasAvatar: true,
+ },
+ emptyContentProps: {
+ contentType: "customers",
+ image: IMAGES.EMPTY_CUSTOMERS,
+ }
+ }
+ ]
+
+ let datePickerFields = [
+ {
+ name: "from_date",
+ label: Lng.t("estimates.fromDate", { locale: language }),
+ onChangeCallback: (formDate, displayDate) => {
+ this.setState({
+ selectedFromDate: displayDate,
+ selectedFromDateValue: formDate
+ })
+ },
+ selectedDate: selectedFromDate,
+ selectedDateValue: selectedFromDateValue
+ },
+ {
+ name: "to_date",
+ label: Lng.t("estimates.toDate", { locale: language }),
+ onChangeCallback: (formDate, displayDate) => {
+ this.setState({
+ selectedToDate: displayDate,
+ selectedToDateValue: formDate
+ })
+ },
+ selectedDate: selectedToDate,
+ selectedDateValue: selectedToDateValue
+ }
+ ]
+
+ let inputFields = [{
+ name: 'estimate_number',
+ hint: Lng.t("estimates.estimateNumber", { locale: language }),
+ inputProps: {
+ autoCapitalize: 'none',
+ autoCorrect: true,
+ }
+ }]
+
+ let dropdownFields = [{
+ name: "filterStatus",
+ label: Lng.t("estimates.status", { locale: language }),
+ fieldIcon: 'align-center',
+ items: FILTER_ESTIMATE_STATUS,
+ onChangeCallback: (val) => {
+ this.setFormField('filterStatus', val)
+ },
+ defaultPickerOptions: {
+ label: Lng.t("estimates.statusPlaceholder", { locale: language }),
+ value: '',
+ },
+ containerStyle: styles.selectPicker
+ }]
+
+ return (
+
+ navigation.navigate(ROUTES.MAIN_MORE),
+ title: Lng.t("header.estimates", { locale: language }),
+ titleStyle: styles.headerTitle,
+ placement: "center",
+ rightIcon: "plus",
+ rightIconPress: () => {
+ this.onAddEstimate()
+ },
+ }}
+ onSearch={this.onSearch}
+ filter
+ filterProps={{
+ onSubmitFilter: handleSubmit(this.onSubmitFilter),
+ selectFields: selectFields,
+ datePickerFields: datePickerFields,
+ inputFields: inputFields,
+ dropdownFields: dropdownFields,
+ clearFilter: this.props
+ }}
+ >
+
+ ),
+ },
+ {
+ Title: ESTIMATES_TABS.SENT,
+ tabName: TAB_NAME(ESTIMATES_TABS.SENT, language, Lng),
+ render: (
+
+ ),
+ },
+ {
+ Title: ESTIMATES_TABS.ALL,
+ tabName: TAB_NAME(ESTIMATES_TABS.ALL, language, Lng),
+ render: (
+
+ ),
+ },
+ ]}
+ />
+
+
+ );
+ }
+}
diff --git a/src/features/estimates/components/Estimates/styles.js b/src/features/estimates/components/Estimates/styles.js
new file mode 100644
index 00000000..5601a433
--- /dev/null
+++ b/src/features/estimates/components/Estimates/styles.js
@@ -0,0 +1,17 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ Tabs: {
+ backgroundColor: colors.veryLightGray,
+ borderBottomRightRadius: 10,
+ borderBottomLeftRadius: 10,
+ },
+ selectPicker: {
+ marginTop: 15,
+ },
+});
diff --git a/src/features/estimates/components/Item/index.js b/src/features/estimates/components/Item/index.js
new file mode 100644
index 00000000..fde62ee2
--- /dev/null
+++ b/src/features/estimates/components/Item/index.js
@@ -0,0 +1,568 @@
+// @flow
+
+import React from 'react';
+import {
+ View,
+ Text,
+ ScrollView,
+ Alert,
+} from 'react-native';
+import styles from './styles';
+import { Field, change } from 'redux-form';
+
+import {
+ InputField,
+ CtDivider,
+ CtButton,
+ DefaultLayout,
+ SelectField,
+ SelectPickerField,
+ CurrencyFormat,
+ RadioButtonGroup,
+} from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import {
+ ITEM_DISCOUNT_OPTION,
+ ITEM_EDIT,
+ ITEM_ADD,
+ ITEM_FORM
+} from '../../constants';
+import { BUTTON_COLOR } from '../../../../api/consts/core';
+import { colors } from '../../../../styles/colors';
+import Lng from '../../../../api/lang/i18n';
+import { ADD_TAX } from '../../../settings/constants';
+import { MAX_LENGTH } from '../../../../api/global';
+import { ITEM_UNITS } from '../../../more/constants';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+export class EstimateItem extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ }
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+
+ navigation.addListener(
+ 'didFocus',
+ payload => {
+ this.forceUpdate();
+ }
+ );
+
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(ITEM_FORM, field, value));
+ };
+
+ saveItem = (values) => {
+ const {
+ addItem,
+ removeEstimateItem,
+ setEstimateItems,
+ itemId,
+ navigation,
+ type,
+ language,
+ } = this.props
+
+ if (this.finalAmount() < 0) {
+ alert(Lng.t("items.lessAmount", { locale: language }))
+
+ return
+ }
+
+ const item = {
+ ...values,
+ title: values.name,
+ final_total: this.finalAmount(),
+ total: this.subTotal(),
+ discount_val: this.totalDiscount(),
+ tax: this.itemTax() + this.itemCompoundTax(),
+ taxes: values.taxes && values.taxes.map(val => {
+ return {
+ ...val,
+ amount: val.compound_tax ?
+ this.getCompoundTaxValue(val.percent) :
+ this.getTaxValue(val.percent),
+ }
+ }),
+ }
+
+ const callback = () => {
+ addItem({
+ item,
+ onResult: () => {
+ navigation.navigate(ROUTES.ESTIMATE)
+ }
+ })
+ }
+
+ if (!itemId) {
+ callback()
+ } else {
+ const estimateItem = [{ ...item, item_id: itemId }]
+
+ if (type === ITEM_EDIT) {
+ removeEstimateItem({ id: itemId })
+ }
+
+ setEstimateItems({ estimateItem })
+
+ navigation.navigate(ROUTES.ESTIMATE)
+ }
+
+ };
+
+ removeItem = () => {
+ const { removeEstimateItem, itemId, navigation, language } = this.props
+
+ Alert.alert(
+ Lng.t("alert.title", { locale: language }),
+ '',
+ [
+ {
+ text: 'OK',
+ onPress: () => {
+ navigation.navigate(ROUTES.ESTIMATE)
+ removeEstimateItem({ id: itemId })
+ }
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+
+ }
+
+ totalDiscount = () => {
+ const { formValues: { discount, discount_type } } = this.props
+
+ let discountPrice = 0
+
+ if (discount_type === 'percentage') {
+ discountPrice = ((discount * this.itemSubTotal()) / 100)
+ } else if (discount_type === 'fixed') {
+ discountPrice = (discount * 100)
+ }
+ else if (discount_type === 'none') {
+ discountPrice = 0
+ this.setFormField('discount', 0)
+ }
+
+ return discountPrice
+ }
+
+ totalAmount = () => {
+ return this.subTotal() + this.itemTax()
+ }
+
+ itemSubTotal = () => {
+ const { formValues: { quantity, price } } = this.props
+
+ subTotal = (price * quantity)
+
+ return subTotal
+ }
+
+ subTotal = () => {
+ return this.itemSubTotal() - this.totalDiscount()
+ }
+
+ itemTax = () => {
+ const { formValues: { taxes } } = this.props
+
+ let totalTax = 0
+
+ taxes && taxes.map(val => {
+ if (!val.compound_tax) {
+ totalTax += this.getTaxValue(val.percent)
+ }
+ })
+
+ return totalTax
+ }
+
+ itemCompoundTax = () => {
+ const { formValues: { taxes } } = this.props
+
+ let totalTax = 0
+
+ taxes && taxes.map(val => {
+ if (val.compound_tax) {
+ totalTax += this.getCompoundTaxValue(val.percent)
+ }
+ })
+
+ return totalTax
+ }
+
+ getTaxValue = (tax) => {
+ return (tax * JSON.parse(this.subTotal())) / 100
+ }
+
+ getCompoundTaxValue = (tax) => {
+ return (tax * JSON.parse(this.totalAmount())) / 100
+ }
+
+ getTaxName = (tax) => {
+ const { taxTypes } = this.props
+ let taxName = ''
+
+ const type = taxTypes.filter(val => val.fullItem.id === tax.tax_type_id)
+
+ if (taxTypes && type.length > 0) {
+ taxName = type[0]['fullItem'].name
+ }
+ return taxName
+ }
+
+ finalAmount = () => {
+ return this.totalAmount() + this.itemCompoundTax()
+ }
+
+ FINAL_AMOUNT = () => {
+ const {
+ language,
+ formValues: { quantity, price, taxes },
+ navigation,
+ discountPerItem,
+ } = this.props
+
+ const currency = navigation.getParam('currency')
+
+ return (
+
+
+
+
+
+
+
+
+
+ {discountPerItem === 'YES' && (
+
+
+
+ {Lng.t("items.finalDiscount", { locale: language })}
+
+
+
+
+
+
+ )}
+
+ {taxes &&
+ taxes.map((val, index) => !val.compound_tax ? (
+
+
+
+ {this.getTaxName(val)} ({val.percent} %)
+
+
+
+
+
+
+ ) : null)
+ }
+
+ {taxes &&
+ taxes.map(val => val.compound_tax ? (
+
+
+
+ {this.getTaxName(val)} ({val.percent} %)
+
+
+
+
+
+
+ ) : null)
+ }
+
+
+
+
+
+
+ {Lng.t("items.finalAmount", { locale: language })}
+
+
+
+
+
+
+
+ )
+ };
+
+ BOTTOM_ACTION = (handleSubmit) => {
+ const { language, loading, type } = this.props
+ const isCreateItem = (type === ITEM_ADD)
+ return (
+
+
+ {!isCreateItem && (
+
+ )}
+
+
+ )
+ }
+
+ render() {
+ const {
+ navigation,
+ handleSubmit,
+ loading,
+ formValues: { discount_type, taxes, discount },
+ initialValues,
+ language,
+ type,
+ discountPerItem,
+ taxTypes,
+ itemId,
+ taxPerItem,
+ } = this.props;
+
+ const isCreateItem = (type === ITEM_ADD)
+ let itemRefs = {}
+
+ return (
+ navigation.navigate(ROUTES.ESTIMATE),
+ title: isCreateItem ?
+ Lng.t("header.addItem", { locale: language }) :
+ Lng.t("header.editItem", { locale: language }),
+ placement: "center",
+ rightIcon: 'save',
+ rightIconProps: {
+ solid: true
+ },
+ rightIconPress: handleSubmit(this.saveItem),
+ }}
+ loadingProps={{
+ is: loading
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ >
+
+ {
+ itemRefs.quantity.focus();
+ }
+ }}
+ />
+
+
+
+ {
+ itemRefs.price.focus();
+ }
+ }}
+ refLinkFn={(ref) => {
+ itemRefs.quantity = ref;
+ }}
+ />
+
+
+ {
+ itemRefs.price = ref;
+ }}
+ isCurrencyInput
+ />
+
+
+
+ {(initialValues.unit || !itemId) && (
+
+ )}
+
+ {discountPerItem == 'YES' && (
+
+
+
+
+
+ )}
+
+ {taxPerItem === 'YES' && (
+ navigation.navigate(ROUTES.TAX, {
+ type: ADD_TAX,
+ onSelect: (val) => {
+ this.setFormField('taxes',
+ [...val, ...taxes]
+ )
+ }
+ })
+ }
+ emptyContentProps={{
+ contentType: "taxes",
+ }}
+ />
+ )}
+
+ {this.FINAL_AMOUNT()}
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/estimates/components/Item/styles.js b/src/features/estimates/components/Item/styles.js
new file mode 100644
index 00000000..12296d78
--- /dev/null
+++ b/src/features/estimates/components/Item/styles.js
@@ -0,0 +1,139 @@
+import { StyleSheet, Platform } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerTitle: {
+ fontSize: 17,
+ color: colors.dark1,
+ },
+ headerContainer: {
+ // backgroundColor: colors.veryLightGray,
+ },
+ taxFakeInput: {
+ color: colors.primary,
+ textAlign: 'right',
+ fontFamily: fonts.poppinsMedium,
+ fontSize: 16,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ dateFieldContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ marginHorizontal: -10
+ },
+ dateField: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingHorizontal: 10
+ },
+ inputFieldStyle: {
+ borderRadius: 0,
+ },
+ inputFieldContainer: {
+ paddingHorizontal: 0,
+ },
+ inputTextStyle: {
+ color: colors.dark3,
+ fontFamily: fonts.poppinsMedium,
+ },
+ inputFieldValidation: {
+ marginHorizontal: 0,
+ },
+ hintStyle: {
+ marginTop: -6,
+ paddingBottom: 6,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+ noteHintStyle: {
+ paddingBottom: 6,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+ fakeInputStyle: {
+ marginTop: 5,
+ },
+ amountContainer: {
+ borderWidth: 0.8,
+ borderColor: colors.lightGray,
+ marginTop: 24,
+ marginBottom: 18,
+ padding: 20,
+ backgroundColor: colors.white,
+ },
+ subContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ label: {
+ color: colors.gray,
+ fontFamily: fonts.poppinsMedium,
+ marginTop: 6,
+ },
+ price: {
+ color: colors.dark2,
+ fontSize: 16,
+ },
+ totalPrice: {
+ color: colors.primary,
+ fontSize: 18,
+ fontFamily: fonts.poppinsMedium,
+ },
+ divider: {
+ backgroundColor: colors.lightGray,
+ borderColor: colors.lightGray,
+ borderWidth: 0.7,
+ marginTop: 10,
+ marginBottom: 8,
+ },
+ submitButton: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ },
+ handleBtn: {
+ marginHorizontal: 9,
+ },
+ buttonContainer: {
+ flex: 1
+ },
+ itemContainer: {
+ marginVertical: 4,
+ borderWidth: 1,
+ borderColor: colors.lightGray
+ },
+ itemLeftTitle: {
+ fontSize: 15,
+ fontFamily: fonts.poppins,
+ color: colors.dark
+ },
+ itemLeftSubTitleLabel: {
+ marginLeft: -6,
+ },
+ itemLeftSubTitle: {
+ color: colors.darkGray,
+ fontSize: 13,
+ },
+ itemRightTitle: {
+ fontFamily: fonts.poppins,
+ fontSize: 18,
+ color: colors.secondary
+ },
+ finalAmountCurrency: {
+ ...Platform.select({
+ android: {
+ marginTop: -5
+ },
+ }),
+ }
+});
diff --git a/src/features/estimates/components/Tab/All.js b/src/features/estimates/components/Tab/All.js
new file mode 100644
index 00000000..94e91cca
--- /dev/null
+++ b/src/features/estimates/components/Tab/All.js
@@ -0,0 +1,115 @@
+// @flow
+import React from 'react';
+import { View, ScrollView, Alert } from 'react-native';
+import { styles } from './styles';
+import { ListView, Content } from '../../../../components';
+import { IMAGES } from '../../../../config';
+import { ESTIMATES_STATUS, ESTIMATE_ADD, ESTIMATES_STATUS_BG_COLOR, ESTIMATES_STATUS_TEXT_COLOR } from '../../constants';
+import { ROUTES } from '../../../../navigation/routes';
+import Lng from '../../../../api/lang/i18n';
+
+type IProps = {
+ estimates: Array,
+ onEstimateSelect: Function,
+ getEstimates: Function,
+ loading: String,
+ canLoadMore: Boolean,
+ refreshing: Boolean,
+ fresh: Boolean,
+ search: String,
+ onAddEstimate: Function,
+ loadMoreItems: Function,
+ filter: Boolean
+};
+
+const All = ({
+ estimates,
+ onEstimateSelect,
+ refreshing,
+ loading,
+ canLoadMore,
+ getEstimates,
+ fresh,
+ search,
+ language,
+ navigation,
+ onAddEstimate,
+ loadMoreItems,
+ filter
+}: IProps) => {
+ let items = [];
+
+ if (typeof estimates !== 'undefined' && estimates.length != 0) {
+ items = estimates.map((item) => {
+ const {
+ estimate_number,
+ user: { name, currency } = {},
+ status,
+ formattedEstimateDate,
+ total,
+ } = item;
+
+ return {
+ title: name,
+ subtitle: {
+ title: estimate_number,
+ label: status,
+ labelBgColor: ESTIMATES_STATUS_BG_COLOR[status],
+ labelTextColor: ESTIMATES_STATUS_TEXT_COLOR[status],
+ },
+ amount: total,
+ currency,
+ rightSubtitle: formattedEstimateDate,
+ fullItem: item,
+ };
+ });
+ }
+
+ let empty = (!filter && !search) ? {
+ description: Lng.t("estimates.empty.all.description", { locale: language }),
+ buttonTitle: Lng.t("estimates.empty.buttonTitle", { locale: language }),
+ buttonPress: () => onAddEstimate()
+ } : {}
+
+ let emptyTitle = search ? Lng.t("search.noResult", { locale: language, search })
+ : (!filter) ? Lng.t("estimates.empty.all.title", { locale: language }) :
+ Lng.t("filter.empty.filterTitle", { locale: language })
+
+ return (
+
+
+ {
+ getEstimates({
+ fresh: true,
+ onResult: onHide,
+ type: '',
+ q: search,
+ resetFilter: true
+ });
+ }}
+ getItems={() => {
+ loadMoreItems({
+ type: '',
+ q: search,
+ });
+ }}
+ bottomDivider
+ emptyContentProps={{
+ title: emptyTitle,
+ image: IMAGES.EMPTY_ESTIMATES,
+ ...empty
+ }}
+ />
+
+
+ );
+};
+
+export default All;
diff --git a/src/features/estimates/components/Tab/Draft.js b/src/features/estimates/components/Tab/Draft.js
new file mode 100644
index 00000000..d2654553
--- /dev/null
+++ b/src/features/estimates/components/Tab/Draft.js
@@ -0,0 +1,114 @@
+// @flow
+import React from 'react';
+import { View, ScrollView, Alert } from 'react-native';
+import { styles } from './styles';
+import { ListView, Content } from '../../../../components';
+import { IMAGES } from '../../../../config';
+import Lng from '../../../../api/lang/i18n';
+import { ESTIMATES_STATUS_BG_COLOR, ESTIMATES_STATUS_TEXT_COLOR } from '../../constants';
+
+type IProps = {
+ estimates: Array,
+ onEstimateSelect: Function,
+ getEstimates: Function,
+ loading: String,
+ canLoadMore: Boolean,
+ refreshing: Boolean,
+ fresh: Boolean,
+ search: String,
+ onAddEstimate: Function,
+ loadMoreItems: Function,
+ filter: Boolean
+};
+
+const Draft = ({
+ estimates,
+ onEstimateSelect,
+ refreshing,
+ loading,
+ canLoadMore,
+ getEstimates,
+ fresh,
+ search,
+ language,
+ navigation,
+ onAddEstimate,
+ loadMoreItems,
+ filter
+}: IProps) => {
+ let items = [];
+
+ if (typeof estimates !== 'undefined' && estimates.length != 0) {
+ items = estimates.map((item) => {
+ const {
+ estimate_number,
+ user: { name, currency } = {},
+ status,
+ formattedEstimateDate,
+ total,
+ } = item;
+
+ return {
+ title: name,
+ subtitle: {
+ title: estimate_number,
+ label: status,
+ labelBgColor: ESTIMATES_STATUS_BG_COLOR[status],
+ labelTextColor: ESTIMATES_STATUS_TEXT_COLOR[status],
+ },
+ amount: total,
+ currency,
+ rightSubtitle: formattedEstimateDate,
+ fullItem: item,
+ };
+ });
+ }
+
+ let empty = (!filter && !search) ? {
+ description: Lng.t("estimates.empty.draft.description", { locale: language }),
+ buttonTitle: Lng.t("estimates.empty.buttonTitle", { locale: language }),
+ buttonPress: () => onAddEstimate(),
+ } : {}
+
+ let emptyTitle = search ? Lng.t("search.noResult", { locale: language, search })
+ : (!filter) ? Lng.t("estimates.empty.draft.title", { locale: language }) :
+ Lng.t("filter.empty.filterTitle", { locale: language })
+
+ return (
+
+
+ {
+ getEstimates({
+ fresh: true,
+ onResult: onHide,
+ type: 'DRAFT',
+ q: search,
+ resetFilter: true
+ });
+ }}
+ getItems={() => {
+ loadMoreItems({
+ type: 'DRAFT',
+ q: search,
+ });
+ }}
+ bottomDivider
+ emptyContentProps={{
+ title: emptyTitle,
+ image: IMAGES.EMPTY_ESTIMATES,
+ ...empty
+ }}
+ />
+
+
+ );
+};
+
+export default Draft;
diff --git a/src/features/estimates/components/Tab/Sent.js b/src/features/estimates/components/Tab/Sent.js
new file mode 100644
index 00000000..d4a95e1b
--- /dev/null
+++ b/src/features/estimates/components/Tab/Sent.js
@@ -0,0 +1,114 @@
+// @flow
+import React from 'react';
+import { View } from 'react-native';
+import { styles } from './styles';
+import { ListView, Content } from '../../../../components';
+import { IMAGES } from '../../../../config';
+import Lng from '../../../../api/lang/i18n';
+import { ESTIMATES_STATUS_BG_COLOR, ESTIMATES_STATUS_TEXT_COLOR } from '../../constants';
+
+type IProps = {
+ estimates: Array,
+ onEstimateSelect: Function,
+ getEstimates: Function,
+ loading: String,
+ canLoadMore: Boolean,
+ refreshing: Boolean,
+ fresh: Boolean,
+ search: String,
+ onAddEstimate: Function,
+ loadMoreItems: Function,
+ filter: Boolean
+};
+
+const Sent = ({
+ estimates,
+ onEstimateSelect,
+ refreshing,
+ loading,
+ canLoadMore,
+ getEstimates,
+ fresh,
+ search,
+ language,
+ navigation,
+ onAddEstimate,
+ loadMoreItems,
+ filter
+}: IProps) => {
+ let items = [];
+
+ if (typeof estimates !== 'undefined' && estimates.length != 0) {
+ items = estimates.map((item) => {
+ const {
+ estimate_number,
+ user: { name, currency } = {},
+ status,
+ formattedEstimateDate,
+ total,
+ } = item;
+
+ return {
+ title: name,
+ subtitle: {
+ title: estimate_number,
+ label: status,
+ labelBgColor: ESTIMATES_STATUS_BG_COLOR[status],
+ labelTextColor: ESTIMATES_STATUS_TEXT_COLOR[status],
+ },
+ amount: total,
+ currency,
+ rightSubtitle: formattedEstimateDate,
+ fullItem: item,
+ };
+ });
+ }
+
+ let empty = (!filter && !search) ? {
+ description: Lng.t("estimates.empty.sent.description", { locale: language }),
+ buttonTitle: Lng.t("estimates.empty.buttonTitle", { locale: language }),
+ buttonPress: () => onAddEstimate(),
+ } : {}
+
+ let emptyTitle = search ? Lng.t("search.noResult", { locale: language, search })
+ : (!filter) ? Lng.t("estimates.empty.sent.title", { locale: language }) :
+ Lng.t("filter.empty.filterTitle", { locale: language })
+
+ return (
+
+
+ {
+ getEstimates({
+ fresh: true,
+ onResult: onHide,
+ type: 'SENT',
+ q: search,
+ resetFilter: true
+ });
+ }}
+ getItems={() => {
+ loadMoreItems({
+ type: 'SENT',
+ q: search,
+ });
+ }}
+ bottomDivider
+ emptyContentProps={{
+ title: emptyTitle,
+ image: IMAGES.EMPTY_ESTIMATES,
+ ...empty
+ }}
+ />
+
+
+ );
+};
+
+export default Sent;
diff --git a/src/features/estimates/components/Tab/styles.js b/src/features/estimates/components/Tab/styles.js
new file mode 100644
index 00000000..274bbb8f
--- /dev/null
+++ b/src/features/estimates/components/Tab/styles.js
@@ -0,0 +1,9 @@
+import { StyleSheet } from 'react-native';
+import { isIPhoneX } from '../../../../api/helper';
+
+export const styles = StyleSheet.create({
+ content: {
+ flex: 1,
+ paddingBottom: isIPhoneX() ? 30 : 0,
+ },
+});
diff --git a/src/features/estimates/components/TemplateField/index.js b/src/features/estimates/components/TemplateField/index.js
new file mode 100644
index 00000000..6a82caf2
--- /dev/null
+++ b/src/features/estimates/components/TemplateField/index.js
@@ -0,0 +1,176 @@
+// @flow
+
+import React, { Component } from 'react';
+import {
+ View,
+} from 'react-native';
+import styles from './styles';
+import { SlideModal, FakeInput, AssetImage, CtButton } from '../../../../components';
+import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
+import { Icon } from 'react-native-elements';
+import { colors } from '../../../../styles/colors';
+import Lng from '../../../../api/lang/i18n';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { headerTitle } from '../../../../api/helper';
+
+type IProps = {
+ label: String,
+ icon: String,
+ onChangeCallback: Function,
+ placeholder: String,
+ containerStyle: Object,
+ rightIcon: String,
+ leftIcon: String,
+ color: String,
+ value: String,
+ templates: Array,
+};
+
+export class TemplateField extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ page: 1,
+ visible: false,
+ selectedTemplate: '',
+ };
+ }
+
+ componentDidMount() {
+ const { input: { value }, templates, navigation } = this.props
+
+ const template = templates.filter(val => val.id === value)[0]
+
+ this.setState({
+ selectedTemplate: template,
+ })
+
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ onToggle = () => {
+ this.setState((prevState) => {
+ return { visible: !prevState.visible }
+ });
+ }
+
+ onTemplateSelect = (template) => {
+ this.setState({
+ selectedTemplate: template
+ })
+ }
+
+ onSearch = (search) => {
+ this.setState({ search })
+ this.getItems({ fresh: true, q: search })
+ }
+
+ onSubmit = () => {
+ const { onChangeCallback, input: { onChange } } = this.props
+
+ const { selectedTemplate } = this.state
+
+ onChange(selectedTemplate.id)
+
+ onChangeCallback && onChangeCallback(selectedTemplate)
+
+ this.onToggle()
+ }
+
+ BOTTOM_ACTION = () => {
+ const { language } = this.props
+
+ return (
+
+
+
+ )
+ }
+
+ render() {
+ const {
+ containerStyle,
+ templates,
+ label,
+ icon,
+ placeholder,
+ meta,
+ language
+ } = this.props;
+
+ const {
+ visible,
+ selectedTemplate: { name, id } = {},
+ } = this.state
+
+ return (
+
+
+
+
+ this.onToggle(),
+ title: Lng.t("header.template", { locale: language }),
+ titleStyle: headerTitle({ marginLeft: -19, marginRight: -19 }),
+ placement: "center",
+ hasCircle: false,
+ noBorder: false,
+ transparent: false,
+ }}
+ bottomDivider
+ defaultLayout
+ bottomAction={this.BOTTOM_ACTION()}
+ >
+
+ {templates.map((val, index) => (
+ this.onTemplateSelect(val)}
+ key={index}
+ >
+
+
+ {id === val.id &&
+
+ }
+
+
+ ))}
+
+
+
+ );
+ }
+}
diff --git a/src/features/estimates/components/TemplateField/styles.js b/src/features/estimates/components/TemplateField/styles.js
new file mode 100644
index 00000000..b7998781
--- /dev/null
+++ b/src/features/estimates/components/TemplateField/styles.js
@@ -0,0 +1,50 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { widthPercentageToDP as wp } from 'react-native-responsive-screen';
+
+export default StyleSheet.create({
+ container: {
+ flex: 1,
+ marginTop: 10,
+ },
+ imageList: {
+ display: 'flex',
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ justifyContent: 'space-between',
+ marginHorizontal: -10
+ },
+ image: {
+ height: 220,
+ width: wp(40),
+ borderWidth: 2,
+ borderColor: colors.lightGray,
+ marginVertical: 10,
+ resizeMode: 'stretch'
+ },
+ active: {
+ borderColor: colors.primary
+ },
+ imageContainer: {
+ position: 'relative',
+ overflow: 'visible',
+ marginHorizontal: 10,
+ },
+ iconStyle: {
+ padding: 3,
+ },
+ submitButton: {
+ // flexDirection: "row",
+ // justifyContent: "space-between",
+ },
+ iconContainer: {
+ position: 'absolute',
+ top: 0,
+ right: -10,
+ zIndex: 100,
+ backgroundColor: colors.primary,
+ borderRadius: 20,
+ padding: 0,
+ margin: 0,
+ }
+});
diff --git a/src/features/estimates/constants.js b/src/features/estimates/constants.js
new file mode 100644
index 00000000..4c41ee53
--- /dev/null
+++ b/src/features/estimates/constants.js
@@ -0,0 +1,235 @@
+import queryString from 'query-string';
+import Lng from "../../api/lang/i18n";
+import { colors } from "../../styles/colors";
+
+// Forms
+// -----------------------------------------
+export const ESTIMATE_SEARCH = 'estimateForm/ESTIMATE_SEARCH';
+export const ESTIMATE_FORM = 'estimateForm/ESTIMATE_EDIT';
+export const ITEM_FORM = 'item/ITEM_FORM';
+
+// Type
+// -----------------------------------------
+export const ESTIMATE_ADD = 'estimateForm/ESTIMATE_ADD';
+export const ESTIMATE_EDIT = 'estimateForm/ESTIMATE_EDIT';
+
+// Actions
+// -----------------------------------------
+export const ESTIMATES_TRIGGER_SPINNER = 'estimate/ESTIMATES_TRIGGER_SPINNER';
+export const GET_ESTIMATES = 'estimate/GET_ESTIMATES';
+export const SET_ESTIMATES = 'estimate/SET_ESTIMATES';
+
+export const CLEAR_ESTIMATES = 'estimate/CLEAR_ESTIMATES';
+export const CLEAR_ESTIMATE = 'estimate/CLEAR_ESTIMATE';
+export const GET_CREATE_ESTIMATE = 'estimate/GET_CREATE_ESTIMATE';
+export const GET_EDIT_ESTIMATE = 'estimate/GET_EDIT_ESTIMATE';
+export const SET_ESTIMATE = 'estimate/SET_ESTIMATE';
+export const SET_EDIT_ESTIMATE = 'estimate/SET_EDIT_ESTIMATE';
+export const CREATE_ESTIMATE = 'estimate/CREATE_ESTIMATE';
+export const EDIT_ESTIMATE = 'estimate/EDIT_ESTIMATE';
+export const CONVERT_TO_INVOICE = 'estimate/CONVERT_TO_INVOICE';
+export const REMOVE_ESTIMATE = 'estimate/REMOVE_ESTIMATE';
+export const REMOVE_FROM_ESTIMATES = 'estimate/REMOVE_FROM_ESTIMATES';
+export const CHANGE_ESTIMATE_STATUS = 'estimate/CHANGE_ESTIMATE_STATUS';
+
+// Items
+// -----------------------------------------
+export const SET_EDIT_ESTIMATE_ITEMS = 'estimate/SET_EDIT_ESTIMATE_ITEMS';
+export const REMOVE_ESTIMATE_ITEM = 'estimate/REMOVE_ESTIMATE_ITEM';
+export const REMOVE_ESTIMATE_ITEMS = 'estimate/REMOVE_ESTIMATE_ITEMS';
+export const ADD_ITEM = 'estimate/ADD_ITEM';
+export const EDIT_ITEM = 'estimate/EDIT_ITEM';
+export const GET_ITEMS = 'estimate/GET_ITEMS';
+export const SET_ITEMS = 'estimate/SET_ITEMS';
+export const SET_ESTIMATE_ITEMS = 'estimate/SET_ESTIMATE_ITEMS';
+export const REMOVE_ITEM = 'estimate/REMOVE_ITEM';
+export const ITEM_ADD = 'estimate/ITEM_ADD';
+export const ITEM_EDIT = 'estimate/ITEM_EDIT';
+
+export const ITEM_DISCOUNT_OPTION = [
+ {
+ key: 'none',
+ label: 'None',
+ },
+ {
+ key: 'fixed',
+ label: 'Fixed',
+ },
+ {
+ key: 'percentage',
+ label: 'Percentage',
+ },
+];
+
+
+export const ESTIMATE_DISCOUNT_OPTION = [
+ {
+ value: 'percentage',
+ displayLabel: '%',
+ label: 'Percentage',
+ },
+];
+
+
+export const ESTIMATES_STATUS_BG_COLOR = {
+ DRAFT: colors.warningLight,
+ SENT: colors.warningLight2,
+ VIEWED: colors.infoLight,
+ EXPIRED: colors.dangerLight,
+ REJECTED: colors.gray2,
+ ACCEPTED: colors.successLight2,
+};
+
+export const ESTIMATES_STATUS_TEXT_COLOR = {
+ DRAFT: colors.warningDark,
+ SENT: colors.warningDark2,
+ VIEWED: colors.infoDark,
+ EXPIRED: colors.dangerDark,
+ REJECTED: colors.darkGray2,
+ ACCEPTED: colors.successDark,
+};
+
+
+export const TAB_NAME = (name, language, Lng) => {
+ return Lng.t(`estimates.tabs.${name}`, { locale: language })
+};
+
+export const ESTIMATES_TABS = {
+ SENT: 'SENT',
+ DRAFT: 'DRAFT',
+ ALL: 'ALL',
+};
+
+// Filter Estimate Mode
+// -----------------------------------------
+export const FILTER_ESTIMATE_STATUS = [
+ { label: 'DRAFT', value: 'DRAFT' },
+ { label: 'SENT', value: 'SENT' },
+ { label: 'VIEWED', value: 'VIEWED' },
+ { label: 'EXPIRED', value: 'EXPIRED' },
+ { label: 'ACCEPTED', value: 'ACCEPTED' },
+ { label: 'REJECTED', value: 'REJECTED' },
+]
+
+export const ESTIMATES_STATUS = {
+ SENT: 'danger',
+ DRAFT: 'warning',
+ PAID: 'success',
+};
+
+
+export const ESTIMATE_ACTIONS = {
+ VIEW: 'download',
+ SEND: 'send',
+ DELETE: 'delete',
+ EDIT: 'edit',
+ CONVERT_TO_INVOICE: 'convertToInvoice',
+ MARK_AS_SENT: 'markAsSent',
+ MARK_AS_ACCEPTED: 'markAsAccepted',
+ MARK_AS_REJECTED: 'markAsRejected',
+}
+
+export const MARK_AS_SENT = 'SENT'
+export const MARK_AS_ACCEPT = 'ACCEPTED'
+export const MARK_AS_REJECT = 'REJECTED'
+
+export const ADD_ESTIMATE_ACTIONS = (language) => {
+
+ return [{
+ label: Lng.t("estimates.actions.sendEstimate", { locale: language }),
+ value: ESTIMATE_ACTIONS.SEND
+ }]
+};
+
+export const EDIT_ESTIMATE_ACTIONS = (language, markAs = '') => {
+
+ const markAsSent = [{
+ label: Lng.t("estimates.actions.markAsSent", { locale: language }),
+ value: ESTIMATE_ACTIONS.MARK_AS_SENT
+ }]
+
+ const markAsAccept = [{
+ label: Lng.t("estimates.actions.markAsAccepted", { locale: language }),
+ value: ESTIMATE_ACTIONS.MARK_AS_ACCEPTED
+ }]
+
+ const markAsReject = [{
+ label: Lng.t("estimates.actions.markAsRejected", { locale: language }),
+ value: ESTIMATE_ACTIONS.MARK_AS_REJECTED
+ }]
+
+ const deleteAction = [{
+ label: Lng.t("estimates.actions.delete", { locale: language }),
+ value: ESTIMATE_ACTIONS.DELETE
+ }]
+
+ const actions = [
+ {
+ label: Lng.t("estimates.actions.convertToInvoice", { locale: language }),
+ value: ESTIMATE_ACTIONS.CONVERT_TO_INVOICE
+ },
+ {
+ label: Lng.t("estimates.actions.sendEstimate", { locale: language }),
+ value: ESTIMATE_ACTIONS.SEND
+ }
+ ]
+
+ let items = []
+
+ if (markAs === MARK_AS_SENT) {
+ items = [
+ ...markAsAccept,
+ ...markAsReject
+ ]
+ }
+ else if (markAs === MARK_AS_ACCEPT) {
+ items = [
+ ...markAsSent,
+ ...markAsReject
+ ]
+ }
+ else if (markAs === MARK_AS_REJECT) {
+ items = [
+ ...markAsSent,
+ ...markAsAccept
+ ]
+ }
+ else {
+ items = [
+ ...markAsSent,
+ ...markAsAccept,
+ ...markAsReject
+ ]
+ }
+
+ return [
+ ...actions,
+ ...items,
+ ...deleteAction
+ ]
+
+};
+
+// Endpoint Api URL
+// -----------------------------------------
+
+export const GET_ESTIMATES_URL = (param) => `estimates?${queryString.stringify({
+ ...param,
+ orderByField: 'created_at',
+ orderBy: 'desc'
+})}`
+
+export const GET_ITEMS_URL = (q, search, page, limit) => `items?search=${q ? q : search}&page=${page}&limit=${limit}`
+
+export const CREATE_ESTIMATE_URL = () => `estimates`
+export const EDIT_ESTIMATE_URL = (estimate) => `estimates/${estimate.id}`
+export const REMOVE_ESTIMATE_URL = (id) => `estimates/${id}`
+export const CHANGE_ESTIMATE_STATUS_URL = (action) => `estimates/${action}`
+
+export const CREATE_ITEM_URL = () => `items`
+export const EDIT_ITEM_URL = (item_id) => `items/${item_id}`
+
+export const GET_EDIT_ESTIMATE_URL = (id) => `estimates/${id}/edit`
+export const GET_CREATE_ESTIMATE_URL = () => `estimates/create`
+
+export const CONVERT_TO_INVOICE_URL = (id) => `estimates/${id}/convert-to-invoice`
\ No newline at end of file
diff --git a/src/features/estimates/containers/Estimate/index.js b/src/features/estimates/containers/Estimate/index.js
new file mode 100644
index 00000000..7fee7f8a
--- /dev/null
+++ b/src/features/estimates/containers/Estimate/index.js
@@ -0,0 +1,83 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Estimate } from '../../components/Estimate';
+import { reduxForm, getFormValues } from 'redux-form';
+import { validate } from './validation';
+import * as EstimatesAction from '../../actions';
+import { ESTIMATE_FORM, ESTIMATE_EDIT } from '../../constants';
+import moment from 'moment';
+import * as CustomersAction from '../../../customers/actions';
+
+const mapStateToProps = (state, { navigation }) => {
+ const {
+ global: { language, taxTypes },
+ estimates: { loading, estimateItems, estimateData, items },
+ customers: { customers },
+ } = state;
+
+ const { estimate = null, nextEstimateNumber, estimateTemplates } = estimateData;
+
+ let type = navigation.getParam('type')
+
+ let isLoading = loading.initEstimateLoading || (type === ESTIMATE_EDIT && !estimate)
+ || !nextEstimateNumber
+
+ return {
+ initLoading: isLoading,
+ loading: loading.estimateLoading,
+ estimateItems,
+ estimateData,
+ items,
+ type,
+ customers,
+ itemsLoading: loading.itemsLoading,
+ language,
+ formValues: getFormValues(ESTIMATE_FORM)(state) || {},
+ taxTypes,
+ initialValues: !isLoading ? {
+ expiry_date: moment().add(7, 'days'),
+ estimate_date: moment(),
+ estimate_number: nextEstimateNumber,
+ discount_type: 'fixed',
+ discount: 0,
+ taxes: [],
+ estimate_template_id: estimateTemplates[0] && estimateTemplates[0].id,
+ ...estimate,
+ customer: estimate && estimate.user,
+ template: estimate && estimate.estimate_template,
+ } : null
+ };
+};
+
+const mapDispatchToProps = {
+ getCreateEstimate: EstimatesAction.getCreateEstimate,
+ createEstimate: EstimatesAction.createEstimate,
+ getItems: EstimatesAction.getItems,
+ getEditEstimate: EstimatesAction.getEditEstimate,
+ editEstimate: EstimatesAction.editEstimate,
+ removeEstimateItems: EstimatesAction.removeEstimateItems,
+ removeEstimate: EstimatesAction.removeEstimate,
+ convertToInvoice: EstimatesAction.convertToInvoice,
+ clearEstimate: EstimatesAction.clearEstimate,
+ convertToInvoice: EstimatesAction.convertToInvoice,
+ changeEstimateStatus: EstimatesAction.changeEstimateStatus,
+ getCustomers: CustomersAction.getCustomers,
+};
+
+// Redux Forms
+const addEstimateReduxForm = reduxForm({
+ form: ESTIMATE_FORM,
+ validate,
+})(Estimate);
+
+// connect
+const EstimateContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(addEstimateReduxForm);
+
+EstimateContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default EstimateContainer;
diff --git a/src/features/estimates/containers/Estimate/validation.js b/src/features/estimates/containers/Estimate/validation.js
new file mode 100644
index 00000000..76f95235
--- /dev/null
+++ b/src/features/estimates/containers/Estimate/validation.js
@@ -0,0 +1,30 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const { estimate_date, estimate_number, expiry_date, estimate_template_id, items, user_id } = values;
+
+ errors.estimate_date = getError(estimate_date, ['required']);
+ errors.expiry_date = getError(expiry_date, ['required']);
+ errors.estimate_number = getError(estimate_number, ['requiredField']);
+
+ errors.items = getError(items, ['requiredCheckArray']);
+
+ errors.user_id = getError(
+ user_id,
+ ['requiredField'],
+ { fieldName: 'Customer' },
+ );
+
+ errors.estimate_template_id = getError(
+ estimate_template_id,
+ ['requiredField'],
+ { fieldName: 'Template' },
+ );
+
+
+ return errors;
+};
diff --git a/src/features/estimates/containers/Estimates/index.js b/src/features/estimates/containers/Estimates/index.js
new file mode 100644
index 00000000..2abfa18a
--- /dev/null
+++ b/src/features/estimates/containers/Estimates/index.js
@@ -0,0 +1,50 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Estimates } from '../../components/Estimates';
+import { reduxForm, getFormValues } from 'redux-form';
+import * as EstimatesAction from '../../actions';
+import { ESTIMATE_SEARCH } from '../../constants';
+import { getCustomers } from '../../../customers/actions';
+
+const mapStateToProps = (state) => {
+
+ const {
+ global: { language },
+ estimates: {
+ estimates,
+ loading: { estimatesLoading }
+ },
+ customers: { customers },
+ } = state;
+
+ return {
+ estimates,
+ customers,
+ loading: estimatesLoading,
+ language,
+ formValues: getFormValues(ESTIMATE_SEARCH)(state) || {},
+ };
+};
+
+const mapDispatchToProps = {
+ getEstimates: EstimatesAction.getEstimates,
+ clearEstimates: EstimatesAction.clearEstimates,
+ getCustomers: getCustomers
+};
+
+// Redux Forms
+const estimateSearchReduxForm = reduxForm({
+ form: ESTIMATE_SEARCH,
+})(Estimates);
+
+// connect
+const EstimatesContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(estimateSearchReduxForm);
+
+EstimatesContainer.navigationOptions = (props) => ({
+ header: null,
+});
+
+export default EstimatesContainer;
diff --git a/src/features/estimates/containers/Item/index.js b/src/features/estimates/containers/Item/index.js
new file mode 100644
index 00000000..9a11c525
--- /dev/null
+++ b/src/features/estimates/containers/Item/index.js
@@ -0,0 +1,66 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { EstimateItem } from '../../components/Item';
+import { reduxForm, getFormValues } from 'redux-form';
+import { validate } from './validation';
+import * as EstimatesAction from '../../actions';
+import { ITEM_FORM } from '../../constants';
+
+const mapStateToProps = (state, { navigation }) => {
+ const {
+ estimates: { loading },
+ global: { language, taxTypes },
+ } = state;
+
+ const item = navigation.getParam('item', {});
+
+ const type = navigation.getParam('type');
+ const discountPerItem = navigation.getParam('discount_per_item');
+ const taxPerItem = navigation.getParam('tax_per_item');
+
+ const isLoading = loading.editItemLoading || loading.removeItemLoading
+
+ return {
+ loading: isLoading,
+ formValues: getFormValues(ITEM_FORM)(state) || {},
+ itemId: item && (item.item_id || item.id),
+ taxTypes,
+ currency: navigation.getParam('currency'),
+ language: language,
+ discountPerItem,
+ taxPerItem,
+ type,
+ initialValues: {
+ price: 0,
+ quantity: 1,
+ discount_type: 'none',
+ discount: 0,
+ taxes: [],
+ ...item
+ },
+ };
+};
+
+const mapDispatchToProps = {
+ addItem: EstimatesAction.addItem,
+ setEstimateItems: EstimatesAction.setEstimateItems,
+ removeEstimateItem: EstimatesAction.removeEstimateItem,
+};
+
+// Redux Forms
+const addItemReduxForm = reduxForm({
+ form: ITEM_FORM,
+ validate,
+})(EstimateItem);
+
+// connect
+const EstimateItemContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(addItemReduxForm);
+
+EstimateItemContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default EstimateItemContainer;
diff --git a/src/features/estimates/containers/Item/validation.js b/src/features/estimates/containers/Item/validation.js
new file mode 100644
index 00000000..dc70d576
--- /dev/null
+++ b/src/features/estimates/containers/Item/validation.js
@@ -0,0 +1,42 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const {
+ name,
+ description,
+ quantity,
+ discount_type,
+ price,
+ discount,
+ } = values;
+
+ errors.name = getError(name, ['requiredField']);
+
+ errors.quantity = getError(
+ quantity,
+ ['minNumberRequired', 'isNumberFormat'],
+ options = { fieldName: 'quantity', minNumber: 0 }
+ );
+
+ errors.price = getError(
+ price,
+ ['minNumberRequired', 'isNumberFormat'],
+ options = { fieldName: 'price', minNumber: 0 }
+ );
+
+ if (discount_type !== 'none') {
+ errors.discount = getError(
+ discount,
+ ['minNumberRequired', 'isNumberFormat'],
+ options = { fieldName: 'discount', minNumber: 0 }
+ );
+ }
+
+ errors.discount_type = getError(discount_type, ['required']);
+
+ return errors;
+};
diff --git a/src/features/estimates/reducers/index.js b/src/features/estimates/reducers/index.js
new file mode 100644
index 00000000..7b88799f
--- /dev/null
+++ b/src/features/estimates/reducers/index.js
@@ -0,0 +1,120 @@
+import {
+ SET_ESTIMATES,
+ CLEAR_ESTIMATES,
+ ESTIMATES_TRIGGER_SPINNER,
+ GET_ESTIMATES,
+ GET_ITEMS,
+ SET_ITEMS,
+ SET_CREATE_ESTIMATE,
+ SET_ESTIMATE_ITEMS,
+ SET_EDIT_ESTIMATE_ITEMS,
+ REMOVE_ESTIMATE_ITEM,
+ SET_EDIT_ESTIMATE,
+ REMOVE_ESTIMATE_ITEMS,
+ CLEAR_ESTIMATE,
+ SET_ESTIMATE,
+ REMOVE_FROM_ESTIMATES
+} from "../constants";
+
+const initialState = {
+ estimates: [],
+ items: [],
+ errors: null,
+ loading: {
+ estimatesLoading: false,
+ itemsLoading: false,
+ estimateLoading: false,
+ initEstimateLoading: false
+ },
+ estimateData: {
+ estimate: null,
+ estimateTemplates: [],
+ nextEstimateNumber: '',
+ },
+ estimateItems: [],
+};
+
+export default function estimatesReducer(state = initialState, action) {
+ const { payload, type } = action;
+
+ switch (type) {
+ case SET_ESTIMATES:
+ let { estimates, fresh, prepend } = payload;
+
+ if (prepend) {
+ return { ...state, estimates: [ ...estimates, ...state.estimates] };
+ }
+
+ if (!fresh) {
+ return { ...state, estimates: [...state.estimates, ...estimates] };
+ }
+
+ return { ...state, estimates };
+
+ case CLEAR_ESTIMATES:
+ return { ...state, estimates: [] };
+
+ case CLEAR_ESTIMATE:
+ return {
+ ...state,
+ estimateItems: [],
+ items: [],
+ estimateData: {
+ estimate: null,
+ estimateTemplates: []
+ }
+ };
+
+ case GET_ESTIMATES:
+ return { ...state };
+
+ case SET_ESTIMATE:
+
+ return { ...state, estimateData: payload };
+
+ case SET_EDIT_ESTIMATE:
+ return { ...state, ...payload };
+
+ case ESTIMATES_TRIGGER_SPINNER:
+ return { ...state, loading: { ...state.loading, ...payload } };
+
+ case SET_ITEMS:
+
+ const { items } = payload;
+
+ if (!payload.fresh) {
+ return { ...state, items: [...state.items, ...items] };
+ }
+ return { ...state, items };
+
+ case SET_ESTIMATE_ITEMS:
+
+ const { estimateItem } = payload;
+
+ return { ...state, estimateItems: [...state.estimateItems, ...estimateItem] };
+
+ case REMOVE_ESTIMATE_ITEM:
+
+ const { id } = payload;
+
+ const estimateItems = state.estimateItems.filter(val => (val.item_id || val.id) !== id)
+
+ return { ...state, estimateItems };
+
+ case REMOVE_ESTIMATE_ITEMS:
+
+ return { ...state, estimateItems: [] };
+
+ case REMOVE_FROM_ESTIMATES:
+
+ const newEstimates = state.estimates.filter(val => val.id !== payload.id)
+
+ return { ...state, estimates: newEstimates };
+
+ case GET_ITEMS:
+ return { ...state };
+
+ default:
+ return state;
+ }
+}
diff --git a/src/features/estimates/saga/index.js b/src/features/estimates/saga/index.js
new file mode 100644
index 00000000..ece502c6
--- /dev/null
+++ b/src/features/estimates/saga/index.js
@@ -0,0 +1,461 @@
+import { call, put, takeEvery } from 'redux-saga/effects';
+import Request from '../../../api/request';
+import {
+ GET_ESTIMATES,
+ GET_CREATE_ESTIMATE,
+ GET_ITEMS,
+ ADD_ITEM,
+ CREATE_ESTIMATE,
+ EDIT_ITEM,
+ REMOVE_ITEM,
+ GET_EDIT_ESTIMATE,
+ EDIT_ESTIMATE,
+ CONVERT_TO_INVOICE,
+ REMOVE_ESTIMATE,
+ CHANGE_ESTIMATE_STATUS,
+ // Endpoint Api URL
+ GET_ESTIMATES_URL,
+ GET_CREATE_ESTIMATE_URL,
+ GET_EDIT_ESTIMATE_URL,
+ CREATE_ITEM_URL,
+ EDIT_ITEM_URL,
+ CREATE_ESTIMATE_URL,
+ EDIT_ESTIMATE_URL,
+ GET_ITEMS_URL,
+ CONVERT_TO_INVOICE_URL,
+ REMOVE_ESTIMATE_URL,
+ CHANGE_ESTIMATE_STATUS_URL,
+} from '../constants';
+import {
+ estimateTriggerSpinner,
+ setEstimates,
+ setItems,
+ setEstimateItems,
+ removeEstimateItem,
+ removeEstimateItems,
+ setEstimate,
+ removeFromEstimates
+} from '../actions';
+import { store } from '../../../store';
+import { setInvoices } from '../../invoices/actions';
+import { ROUTES } from '../../../navigation/routes';
+
+
+function* getEstimates(payloadData) {
+
+ const {
+ payload: {
+ onResult = null,
+ fresh = true,
+ type = '',
+ onMeta = null,
+ params = null,
+ pagination: { page = 1, limit = 10 } = {},
+ } = {},
+ } = payloadData;
+
+ yield put(estimateTriggerSpinner({ estimatesLoading: true }));
+
+ try {
+
+ let param = {
+ ...params,
+ status: type,
+ page,
+ limit
+ }
+ const options = {
+ path: GET_ESTIMATES_URL(param),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setEstimates({ estimates: response.estimates.data, fresh }));
+
+ onMeta && onMeta(response.estimates);
+
+ onResult && onResult(true);
+
+ } catch (error) {
+ onResult && onResult(false);
+ } finally {
+ yield put(estimateTriggerSpinner({ estimatesLoading: false }));
+ }
+}
+
+function* getCreateEstimate(payloadData) {
+ const {
+ payload: { onResult },
+ } = payloadData;
+
+ yield put(estimateTriggerSpinner({ initEstimateLoading: true }));
+
+ try {
+
+
+ const options = {
+ path: GET_CREATE_ESTIMATE_URL(),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setEstimate(response));
+
+ onResult && onResult(response);
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(estimateTriggerSpinner({ initEstimateLoading: false }));
+ }
+}
+
+function* getEditEstimate(payloadData) {
+ const {
+ payload: { id, onResult },
+ } = payloadData;
+
+ yield put(estimateTriggerSpinner({ initEstimateLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_EDIT_ESTIMATE_URL(id),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setEstimate(response));
+
+ yield put(removeEstimateItems());
+
+ yield put(setEstimateItems({ estimateItem: response.estimate.items }));
+
+ onResult && onResult(response.estimate);
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(estimateTriggerSpinner({ initEstimateLoading: false }));
+ }
+}
+
+function* addItem(payloadData) {
+ const {
+ payload: {
+ item,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(estimateTriggerSpinner({ createEstimateItemLoading: true }));
+
+ try {
+
+ const { price, name, description, taxes, unit } = item
+
+ const options = {
+ path: CREATE_ITEM_URL(),
+ body: {
+ name,
+ description,
+ price,
+ taxes,
+ unit,
+ }
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ const estimateItem = [{
+ ...response.item,
+ item_id: response.item.id,
+ ...item
+ }]
+
+ yield put(setEstimateItems({ estimateItem }));
+
+ onResult && onResult()
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(estimateTriggerSpinner({ createEstimateItemLoading: false }));
+ }
+}
+
+function* editItem(payloadData) {
+ const {
+ payload: {
+ item,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(estimateTriggerSpinner({ estimateLoading: true }));
+
+ try {
+
+ const { price, name, description, item_id } = item
+
+ const options = {
+ path: EDIT_ITEM_URL(item_id),
+ body: {
+ name,
+ description,
+ price,
+ }
+ };
+
+ const response = yield call([Request, 'put'], options);
+
+ const estimateItem = [{
+ ...response.item,
+ ...item,
+ }]
+
+ yield put(removeEstimateItem({ id: estimateItem.id }));
+
+ yield put(setEstimateItems({ estimateItem }));
+
+ onResult && onResult()
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(estimateTriggerSpinner({ estimateLoading: false }));
+ }
+}
+
+function* createEstimate(payloadData) {
+ const {
+ payload: {
+ estimate,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(estimateTriggerSpinner({ estimateLoading: true }));
+
+ try {
+
+ const options = {
+ path: CREATE_ESTIMATE_URL(),
+ body: estimate,
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ if (!(response.error)) {
+ yield put(removeEstimateItems())
+
+ yield put(setEstimates({ estimates: [response.estimate], prepend: true }));
+
+ onResult && onResult(response.url)
+ }
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(estimateTriggerSpinner({ estimateLoading: false }));
+ }
+}
+
+function* editEstimate(payloadData) {
+ const {
+ payload: {
+ estimate,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(estimateTriggerSpinner({ estimateLoading: true }));
+
+ try {
+
+ const options = {
+ path: EDIT_ESTIMATE_URL(estimate),
+ body: estimate
+ };
+
+ const response = yield call([Request, 'put'], options);
+
+ yield put(removeFromEstimates({ id: estimate.id }))
+
+ yield put(setEstimates({ estimates: [response.estimate], prepend: true }));
+
+ onResult && onResult(response.url)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(estimateTriggerSpinner({ estimateLoading: false }));
+ }
+}
+
+function* getItems(payloadData) {
+ const {
+ payload: {
+ onResult,
+ fresh,
+ onMeta,
+ search = '',
+ q = '',
+ pagination: { page, limit },
+ },
+ } = payloadData;
+
+ yield put(estimateTriggerSpinner({ itemsLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_ITEMS_URL(q, search, page, limit),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setItems({ items: response.items.data, fresh }));
+
+ onMeta && onMeta(response.items);
+
+ onResult && onResult(response.items);
+ } catch (error) {
+ // console.log(error);
+ onResult && onResult(response.items);
+ } finally {
+ yield put(estimateTriggerSpinner({ itemsLoading: false }));
+ }
+}
+
+function* removeItem(payloadData) {
+ const {
+ payload: {
+ onResult,
+ id,
+ },
+ } = payloadData;
+
+ yield put(estimateTriggerSpinner({ removeItemLoading: true }));
+
+ try {
+
+
+ yield put(removeEstimateItem({ id }));
+
+ onResult && onResult();
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(estimateTriggerSpinner({ removeItemLoading: false }));
+ }
+}
+
+function* convertToInvoice(payloadData) {
+ const {
+ payload: {
+ onResult,
+ id,
+ },
+ } = payloadData;
+
+ yield put(estimateTriggerSpinner({ estimateLoading: true }));
+
+ try {
+
+ const options = {
+ path: CONVERT_TO_INVOICE_URL(id),
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ yield put(removeEstimateItems())
+
+ yield put(setInvoices({ invoices: [response.invoice], prepend: true }));
+
+ onResult && onResult();
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(estimateTriggerSpinner({ estimateLoading: false }));
+ }
+}
+
+function* removeEstimate(payloadData) {
+ const {
+ payload: {
+ onResult,
+ id,
+ },
+ } = payloadData;
+
+ yield put(estimateTriggerSpinner({ estimateLoading: true }));
+
+ try {
+
+ const options = {
+ path: REMOVE_ESTIMATE_URL(id),
+ };
+
+ yield call([Request, 'delete'], options);
+
+ yield put(removeFromEstimates({ id }))
+
+ onResult && onResult();
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(estimateTriggerSpinner({ estimateLoading: false }));
+ }
+}
+
+function* changeEstimateStatus(payloadData) {
+ const {
+ payload: {
+ onResult,
+ id,
+ action,
+ navigation
+ },
+ } = payloadData;
+
+ yield put(estimateTriggerSpinner({ estimateLoading: true }));
+
+ try {
+
+ const options = {
+ path: CHANGE_ESTIMATE_STATUS_URL(action),
+ body: { id }
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ if (response.success) {
+ navigation.navigate(ROUTES.ESTIMATE_LIST)
+ yield call(getEstimates, payload = {});
+ }
+
+
+ onResult && onResult();
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(estimateTriggerSpinner({ estimateLoading: false }));
+ }
+}
+
+export default function* estimatesSaga() {
+ yield takeEvery(GET_ESTIMATES, getEstimates);
+ yield takeEvery(GET_CREATE_ESTIMATE, getCreateEstimate);
+ yield takeEvery(GET_EDIT_ESTIMATE, getEditEstimate);
+ yield takeEvery(ADD_ITEM, addItem);
+ yield takeEvery(GET_ITEMS, getItems);
+ yield takeEvery(CREATE_ESTIMATE, createEstimate);
+ yield takeEvery(EDIT_ESTIMATE, editEstimate);
+ yield takeEvery(EDIT_ITEM, editItem);
+ yield takeEvery(REMOVE_ITEM, removeItem);
+ yield takeEvery(CONVERT_TO_INVOICE, convertToInvoice);
+ yield takeEvery(CHANGE_ESTIMATE_STATUS, changeEstimateStatus);
+ yield takeEvery(REMOVE_ESTIMATE, removeEstimate);
+}
diff --git a/src/features/expenses/actions/index.js b/src/features/expenses/actions/index.js
new file mode 100644
index 00000000..b4b63445
--- /dev/null
+++ b/src/features/expenses/actions/index.js
@@ -0,0 +1,93 @@
+
+import {
+ GET_CATEGORIES,
+ SET_CATEGORIES,
+ EXPENSES_TRIGGER_SPINNER,
+ GET_EXPENSES,
+ SET_EXPENSES,
+ CREATE_EXPENSE,
+ CLEAR_EXPENSE,
+ GET_EDIT_EXPENSE,
+ SET_EXPENSE,
+ GET_CREATE_EXPENSE,
+ EDIT_EXPENSE,
+ SET_FILTER_EXPENSES,
+ REMOVE_EXPENSE,
+ GET_RECEIPT,
+ DOWNLOAD_RECEIPT
+} from "../constants";
+
+export const getCategories = (payload = {}) => ({
+ type: GET_CATEGORIES,
+ payload,
+});
+
+export const setCategories = (payload = {}) => ({
+ type: SET_CATEGORIES,
+ payload,
+});
+
+export const getExpenses = (payload = {}) => ({
+ type: GET_EXPENSES,
+ payload,
+});
+
+export const setFilterExpenses = (payload = {}) => ({
+ type: SET_FILTER_EXPENSES,
+ payload,
+});
+
+export const setExpenses = (payload = {}) => ({
+ type: SET_EXPENSES,
+ payload,
+});
+
+export const createExpense = (payload = {}) => ({
+ type: CREATE_EXPENSE,
+ payload,
+});
+
+export const expenseTriggerSpinner = (payload) => ({
+ type: EXPENSES_TRIGGER_SPINNER,
+ payload,
+});
+
+export const clearExpense = (payload) => ({
+ type: CLEAR_EXPENSE,
+ payload,
+});
+
+export const getCreateExpense = (payload) => ({
+ type: GET_CREATE_EXPENSE,
+ payload,
+});
+
+export const getEditExpense = (payload) => ({
+ type: GET_EDIT_EXPENSE,
+ payload,
+});
+
+export const editExpense = (payload) => ({
+ type: EDIT_EXPENSE,
+ payload,
+});
+
+export const setExpense = (payload) => ({
+ type: SET_EXPENSE,
+ payload,
+});
+
+export const removeExpense = (payload = {}) => ({
+ type: REMOVE_EXPENSE,
+ payload,
+});
+
+export const getReceipt = (payload = {}) => ({
+ type: GET_RECEIPT,
+ payload,
+});
+
+export const downloadReceipt = (payload = {}) => ({
+ type: DOWNLOAD_RECEIPT,
+ payload,
+});
diff --git a/src/features/expenses/components/Expense/index.js b/src/features/expenses/components/Expense/index.js
new file mode 100644
index 00000000..38855815
--- /dev/null
+++ b/src/features/expenses/components/Expense/index.js
@@ -0,0 +1,372 @@
+// @flow
+
+import React from 'react';
+import { View, Alert } from 'react-native';
+import { Field, change } from 'redux-form';
+import styles from './styles';
+import {
+ InputField,
+ CtButton,
+ DefaultLayout,
+ FilePicker,
+ SelectPickerField,
+ DatePickerField,
+} from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import { EXPENSE_FORM, EXPENSE_ADD, EXPENSE_EDIT, EXPENSE_ACTIONS, ACTIONS_VALUE } from '../../constants';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import Lng from '../../../../api/lang/i18n';
+import { CATEGORY_ADD } from '../../../settings/constants';
+import { Linking } from 'expo';
+import moment from 'moment';
+import { MAX_LENGTH } from '../../../../api/global';
+
+const IMAGE_TYPE = 'image/jpeg'
+
+export class Expense extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ attachmentReceipt: null,
+ isLoading: true,
+ imageUrl: null,
+ newCategory: [],
+ fileLoading: false,
+ fileType: IMAGE_TYPE
+ };
+ }
+
+ componentDidMount() {
+
+ const {
+ navigation,
+ getCreateExpense,
+ type,
+ getEditExpense,
+ getReceipt,
+ language,
+ } = this.props
+
+ if (type === EXPENSE_EDIT) {
+ let id = navigation.getParam('id', null)
+
+ getEditExpense({
+ id,
+ onResult: ({ media }) => {
+ media.length !== 0 ?
+ getReceipt({
+ id,
+ onResult: ({ image, type }) => {
+ this.setState({
+ isLoading: false,
+ imageUrl: image,
+ fileType: type.toLowerCase()
+ })
+ }
+ }) :
+ this.setState({ isLoading: false })
+ }
+ })
+ } else {
+ getCreateExpense({
+ onResult: ({ categories }) => {
+
+ if (typeof categories === 'undefined' || categories.length === 0) {
+ Alert.alert(
+ Lng.t("expenses.noCategories", { locale: language }),
+ '',
+ [
+ {
+ text: 'Add',
+ onPress: () => {
+ navigation.navigate(ROUTES.CATEGORY, {
+ type: CATEGORY_ADD,
+ onSelect: (val) => {
+ this.setNewCategory(val)
+ }
+ })
+ }
+ },
+ {
+ text: 'Cancel',
+ onPress: () => navigation.goBack(null),
+ style: 'cancel',
+ }
+ ],
+ { cancelable: false }
+ );
+ }
+ else {
+ this.setState({ isLoading: false })
+ }
+ }
+ })
+ }
+
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ this.props.clearExpense()
+ goBack(UNMOUNT)
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(EXPENSE_FORM, field, value));
+ };
+
+ onSubmitExpense = (value) => {
+ const {
+ createExpense,
+ navigation,
+ clearExpense,
+ editExpense,
+ type,
+ loading
+ } = this.props
+ const { attachmentReceipt, fileLoading } = this.state
+
+ if (!fileLoading && !loading) {
+ type === EXPENSE_ADD ?
+ createExpense({
+ params: value,
+ attachmentReceipt,
+ onResult: () => {
+ navigation.navigate(ROUTES.MAIN_EXPENSES)
+ clearExpense()
+ }
+ }) :
+ editExpense({
+ params: value,
+ id: navigation.getParam('id'),
+ attachmentReceipt,
+ onResult: () => {
+ navigation.navigate(ROUTES.MAIN_EXPENSES)
+ clearExpense()
+ }
+ })
+ }
+
+ };
+
+ removeExpense = () => {
+ const { removeExpense, navigation, language } = this.props
+
+ Alert.alert(
+ Lng.t("alert.title", { locale: language }),
+ Lng.t("expenses.alertDescription", { locale: language }),
+ [
+ {
+ text: 'OK',
+ onPress: () => removeExpense({
+ id: navigation.getParam('id', null),
+ navigation
+ })
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ }
+
+ onOptionSelect = (action) => {
+ const { company: { unique_hash }, endpointURL, navigation } = this.props
+
+ if (action == ACTIONS_VALUE.REMOVE) {
+ this.removeExpense()
+ } else if (action == ACTIONS_VALUE.DOWNLOAD) {
+ const id = navigation.getParam('id')
+ Linking.openURL(`${endpointURL}/expenses/${id}/receipt/${unique_hash}`)
+ }
+ }
+
+ setNewCategory = (val) => {
+ this.setState({
+ newCategory: val,
+ isLoading: false
+ })
+ this.setFormField('expense_category_id', val.id)
+ this.setFormField('expense_date', moment())
+ }
+
+ getCategoryList = ({ name, id }) => {
+ let Category = { label: name, value: id }
+ return Category
+ }
+
+ BOTTOM_ACTION = (handleSubmit) => {
+ const { loading, language } = this.props
+ const { fileLoading } = this.state
+
+ return (
+
+
+
+ )
+ }
+
+
+ render() {
+
+ const {
+ navigation,
+ handleSubmit,
+ initLoading,
+ categories,
+ language,
+ type,
+ clearExpense,
+ formValues
+ } = this.props;
+
+ const { imageUrl, isLoading, newCategory, fileType } = this.state;
+
+ let CategoriesName = []
+
+ if (typeof categories !== 'undefined' && categories.length != 0) {
+ CategoriesName = categories.map((category) => {
+ return this.getCategoryList(category)
+ })
+ }
+
+ if (newCategory && newCategory.length !== 0)
+ CategoriesName.push(this.getCategoryList(newCategory))
+
+
+ const isCreateExpense = (type === EXPENSE_ADD)
+
+ let expenseRefs = {}
+ let newCategoryLoading = !(newCategory && newCategory.length === 0)
+ let loading = !newCategoryLoading ? (initLoading || isLoading) : false
+
+ let drownDownProps = (type === EXPENSE_EDIT && !loading) ? {
+ options: EXPENSE_ACTIONS(Lng, language, imageUrl),
+ onSelect: this.onOptionSelect,
+ cancelButtonIndex: imageUrl ? 2 : 1,
+ destructiveButtonIndex: imageUrl ? 1 : 2
+ } : null
+
+ return (
+ {
+ navigation.goBack(null)
+ clearExpense()
+ },
+ title: isCreateExpense ?
+ Lng.t("header.addExpense", { locale: language }) :
+ Lng.t("header.editExpense", { locale: language }),
+ placement: "center",
+ rightIcon: isCreateExpense ? 'save' : null,
+ rightIconPress: handleSubmit(this.onSubmitExpense),
+ rightIconProps: {
+ solid: true
+ }
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ loadingProps={{
+ is: loading
+ }}
+ dropdownProps={drownDownProps}
+ >
+
+
+
+ {fileType === IMAGE_TYPE && (
+
+ this.setState({ attachmentReceipt: val })
+ }
+ imageUrl={imageUrl}
+ containerStyle={styles.filePicker}
+ fileLoading={(val) => {
+ this.setState({ fileLoading: val })
+ }}
+ />
+ )}
+
+ {!loading && formValues.expense_date && ( )}
+
+
+ {
+ expenseRefs.category.focus();
+ }
+ }}
+ isCurrencyInput
+ inputFieldStyle={styles.inputFieldStyle}
+ />
+
+ {
+ this.setFormField('expense_category_id', val)
+ }}
+ defaultPickerOptions={{
+ label: Lng.t("expenses.categoryPlaceholder", { locale: language }),
+ value: '',
+ }}
+ containerStyle={styles.selectPicker}
+ refLinkFn={(ref) => {
+ expenseRefs.category = ref;
+ }}
+ onDonePress={() => expenseRefs.notes.focus()}
+ />
+
+ {
+ expenseRefs.notes = ref;
+ }}
+ />
+
+
+
+ );
+ }
+}
+
diff --git a/src/features/expenses/components/Expense/styles.js b/src/features/expenses/components/Expense/styles.js
new file mode 100644
index 00000000..6fdb27db
--- /dev/null
+++ b/src/features/expenses/components/Expense/styles.js
@@ -0,0 +1,26 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+import { isIPhoneX } from '../../../../api/helper';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ submitButton: {
+ // display: 'flex',
+ paddingHorizontal: 10,
+ },
+ selectPicker: {
+ marginTop: 20,
+ },
+ filePicker: {
+ marginBottom: 20,
+ marginTop: 4,
+ }
+});
diff --git a/src/features/expenses/components/Expenses/index.js b/src/features/expenses/components/Expenses/index.js
new file mode 100644
index 00000000..24ba0cc8
--- /dev/null
+++ b/src/features/expenses/components/Expenses/index.js
@@ -0,0 +1,402 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import { change } from 'redux-form';
+import styles from './styles';
+import { MainLayout, ListView } from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import { IMAGES } from '../../../../config';
+import Lng from '../../../../api/lang/i18n';
+import { EXPENSE_ADD, EXPENSE_EDIT, EXPENSE_SEARCH } from '../../constants';
+import moment from 'moment';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+
+let params = {
+ search: '',
+ expense_category_id: '',
+ from_date: '',
+ to_date: '',
+}
+
+type IProps = {
+ navigation: Object,
+ getExpenses: Function,
+ expenses: Object,
+ loading: Boolean,
+ language: String,
+}
+
+export class Expenses extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ refreshing: false,
+ fresh: true,
+ pagination: {
+ page: 1,
+ limit: 10,
+ lastPage: 1,
+ },
+ search: '',
+ selectedCategory: '',
+ filter: false,
+ selectedFromDate: '',
+ selectedToDate: '',
+ selectedFromDateValue: '',
+ selectedToDateValue: ''
+ };
+ }
+
+ componentDidMount() {
+ const { categories, getCategories, navigation } = this.props
+
+ // Call Categories Api if empty
+ let hasCategoriesApiCalled = categories ? (typeof categories === 'undefined' || categories.length === 0) : true
+
+ hasCategoriesApiCalled && getCategories()
+
+ goBack(MOUNT, navigation, ROUTES.MAIN_INVOICES)
+ }
+
+ componentWillUpdate(nextProps, nextState) {
+
+ const { navigation } = nextProps
+ const pagination = navigation.getParam('pagination', null)
+ const apiCall = navigation.getParam('apiCall', false)
+
+ if (pagination && !(apiCall)) {
+ navigation.setParams({ 'pagination': null, apiCall: true })
+
+ const { last_page, current_page } = pagination
+ this.setState({
+ pagination: {
+ ...this.state.pagination,
+ lastPage: last_page,
+ page: current_page + 1,
+ }
+ });
+ }
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ onExpenseSelect = ({ id }) => {
+ const { navigation } = this.props
+
+ navigation.navigate(ROUTES.EXPENSE, { type: EXPENSE_EDIT, id })
+ this.onResetFilter()
+ }
+
+
+ getItems = ({
+ fresh = false,
+ onResult,
+ filter = false,
+ params,
+ } = {}) => {
+ const { getExpenses } = this.props;
+ const { refreshing, pagination } = this.state;
+
+ if (refreshing) {
+ return;
+ }
+
+ this.setState({
+ refreshing: true,
+ fresh,
+ });
+
+ const paginationParams = fresh ? { ...pagination, page: 1 } : pagination;
+
+ if (!fresh && paginationParams.lastPage < paginationParams.page) {
+ return;
+ }
+
+ getExpenses({
+ fresh,
+ pagination: paginationParams,
+ params,
+ filter,
+ onMeta: ({ last_page, current_page }) => {
+ this.setState({
+ pagination: {
+ ...paginationParams,
+ lastPage: last_page,
+ page: current_page + 1,
+ },
+ });
+ },
+ onResult: (val) => {
+ this.setState({
+ refreshing: false,
+ fresh: !val,
+ });
+ onResult && onResult();
+ },
+ });
+ };
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(EXPENSE_SEARCH, field, value));
+
+ if (field === 'expense_category_id')
+ this.setState({ selectedCategory: value })
+ };
+
+ onSearch = (search) => {
+ this.onResetFilter()
+ this.setState({ search })
+ this.getItems({ fresh: true, params: { ...params, search } })
+ };
+
+ onResetFilter = () => {
+ this.setState({ filter: false })
+ }
+
+ onSubmitFilter = ({ from_date, to_date, expense_category_id }) => {
+
+ if (from_date || to_date || expense_category_id) {
+ this.setState({ filter: true })
+
+ this.getItems({
+ fresh: true,
+ params: {
+ ...params,
+ expense_category_id,
+ from_date,
+ to_date,
+ },
+ filter: true
+ })
+ }
+ else
+ this.onResetFilter()
+ }
+
+
+ loadMoreItems = () => {
+ const { search, filter } = this.state
+ const {
+ formValues: {
+ from_date = '',
+ to_date = '',
+ expense_category_id = ''
+ }
+ } = this.props
+
+
+ if (filter) {
+
+ this.getItems({
+ params: {
+ ...params,
+ expense_category_id,
+ from_date,
+ to_date,
+ },
+ filter: true
+ })
+ }
+ else
+ this.getItems({ params: { ...params, search } });
+
+ }
+
+ getExpensesList = (expenses) => {
+ let expensesList = []
+ const { currency } = this.props
+
+ if (typeof expenses !== 'undefined' && expenses.length != 0) {
+ expensesList = expenses.map((expense) => {
+ const {
+ notes,
+ formattedExpenseDate,
+ amount,
+ category
+ } = expense;
+
+ return {
+ title: category.name ? category.name[0].toUpperCase() +
+ category.name.slice(1) : '',
+ subtitle: {
+ title: notes,
+ },
+ amount,
+ currency,
+ rightSubtitle: formattedExpenseDate,
+ fullItem: expense,
+ };
+ });
+ }
+ return expensesList
+ }
+
+ getCategoriesList = (categories) => {
+ let CategoriesList = []
+ if (typeof categories !== 'undefined' && categories.length != 0) {
+ CategoriesList = categories.map((category) => {
+ return {
+ label: category.name,
+ value: category.id
+ }
+ })
+ }
+ return CategoriesList
+ }
+
+ render() {
+
+ const {
+ navigation,
+ expenses,
+ filterExpenses,
+ loading,
+ language,
+ currency,
+ handleSubmit,
+ categories,
+ } = this.props;
+
+ const {
+ refreshing,
+ pagination: { lastPage, page },
+ fresh,
+ search,
+ filter,
+ selectedCategory,
+ selectedFromDate,
+ selectedToDate,
+ selectedFromDateValue,
+ selectedToDateValue
+ } = this.state;
+
+ const canLoadMore = lastPage >= page;
+
+ let expensesItem = this.getExpensesList(expenses);
+ let filterExpensesItem = this.getExpensesList(filterExpenses);
+ let CategoriesList = this.getCategoriesList(categories)
+
+
+ let dropdownFields = [{
+ name: "expense_category_id",
+ label: Lng.t("expenses.category", { locale: language }),
+ fieldIcon: 'align-center',
+ items: CategoriesList,
+ onChangeCallback: (val) => {
+ this.setFormField('expense_category_id', val)
+ },
+ defaultPickerOptions: {
+ label: Lng.t("expenses.categoryPlaceholder", { locale: language }),
+ value: '',
+ },
+ selectedItem: selectedCategory,
+ containerStyle: styles.selectPicker
+ }]
+
+ let datePickerFields = [
+ {
+ name: "from_date",
+ label: Lng.t("expenses.fromDate", { locale: language }),
+ onChangeCallback: (formDate, displayDate) => {
+ this.setState({
+ selectedFromDate: displayDate,
+ selectedFromDateValue: formDate
+ })
+ },
+ selectedDate: selectedFromDate,
+ selectedDateValue: selectedFromDateValue
+ },
+ {
+ name: "to_date",
+ label: Lng.t("expenses.toDate", { locale: language }),
+ onChangeCallback: (formDate, displayDate) => {
+ this.setState({
+ selectedToDate: displayDate,
+ selectedToDateValue: formDate
+ })
+ },
+ selectedDate: selectedToDate,
+ selectedDateValue: selectedToDateValue
+ }
+ ]
+
+ let empty = (!filter && !search) ? {
+ description: Lng.t("expenses.empty.description", { locale: language }),
+ buttonTitle: Lng.t("expenses.empty.buttonTitle", { locale: language }),
+ buttonPress: () => {
+ navigation.navigate(ROUTES.EXPENSE, { type: EXPENSE_ADD })
+ this.onResetFilter()
+ }
+ } : {}
+
+ let emptyTitle = search ? Lng.t("search.noResult", { locale: language, search })
+ : (!filter) ? Lng.t("expenses.empty.title", { locale: language }) :
+ Lng.t("filter.empty.filterTitle", { locale: language })
+
+ let isLoading = navigation.getParam('loading', false)
+
+ return (
+
+ {
+ navigation.navigate(ROUTES.EXPENSE, { type: EXPENSE_ADD })
+ this.onResetFilter()
+ },
+ title: Lng.t("header.expenses", { locale: language })
+ }}
+ onSearch={this.onSearch}
+ bottomDivider
+ filter
+ filterProps={{
+ onSubmitFilter: handleSubmit(this.onSubmitFilter),
+ datePickerFields: datePickerFields,
+ dropdownFields: dropdownFields,
+ clearFilter: this.props,
+ language: language
+ }}
+ loadingProps={{ is: isLoading || (loading && fresh) }}
+ >
+
+
+ {
+ this.onResetFilter()
+ this.getItems({
+ fresh: true,
+ onResult: onHide,
+ params: { ...params, search }
+ });
+ }}
+ getItems={() => {
+ this.loadMoreItems()
+ }}
+ contentContainerStyle={{ flex: 2 }}
+ bottomDivider
+ emptyContentProps={{
+ title: emptyTitle,
+ image: IMAGES.EMPTY_EXPENSES,
+ ...empty
+ }}
+ leftSubTitleStyle={{ textAlign: "justify" }}
+ />
+
+
+
+
+ );
+ }
+}
+
diff --git a/src/features/expenses/components/Expenses/styles.js b/src/features/expenses/components/Expenses/styles.js
new file mode 100644
index 00000000..3d2b8365
--- /dev/null
+++ b/src/features/expenses/components/Expenses/styles.js
@@ -0,0 +1,18 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ listViewContainer: {
+ flex: 1,
+ },
+ selectPicker: {
+ marginTop: 15,
+ },
+});
diff --git a/src/features/expenses/constants.js b/src/features/expenses/constants.js
new file mode 100644
index 00000000..eafbe2d2
--- /dev/null
+++ b/src/features/expenses/constants.js
@@ -0,0 +1,79 @@
+import queryString from 'query-string';
+
+// Forms
+// -----------------------------------------
+export const EXPENSE_SEARCH = 'expenses/EXPENSE_SEARCH';
+export const EXPENSE_FORM = 'expenses/EXPENSE_FORM';
+
+
+// Type
+// -----------------------------------------
+export const EXPENSE_ADD = 'expense/EXPENSE_ADD';
+export const EXPENSE_EDIT = 'expense/EXPENSE_EDIT';
+
+// Actions
+// -----------------------------------------
+export const GET_CATEGORIES = 'category/GET_CATEGORIES';
+export const SET_CATEGORIES = 'category/SET_CATEGORIES';
+
+export const GET_EXPENSES = 'expenses/GET_EXPENSES';
+export const SET_FILTER_EXPENSES = 'expenses/SET_FILTER_EXPENSES';
+export const GET_CREATE_EXPENSE = 'expenses/GET_CREATE_EXPENSE';
+export const GET_EDIT_EXPENSE = 'expenses/GET_EDIT_EXPENSE';
+export const EDIT_EXPENSE = 'expenses/EDIT_EXPENSE';
+export const SET_EXPENSES = 'expenses/SET_EXPENSES';
+export const SET_EXPENSE = 'expenses/SET_EXPENSE';
+export const CLEAR_EXPENSE = 'expenses/CLEAR_EXPENSE';
+export const REMOVE_EXPENSE = 'expenses/REMOVE_EXPENSE';
+export const CREATE_EXPENSE = 'expenses/CREATE_EXPENSE';
+export const GET_RECEIPT = 'expenses/GET_RECEIPT';
+export const DOWNLOAD_RECEIPT = 'expenses/DOWNLOAD_RECEIPT';
+
+export const EXPENSES_TRIGGER_SPINNER = 'expenses/EXPENSES_TRIGGER_SPINNER';
+
+
+export const ACTIONS_VALUE = {
+ REMOVE: 'remove',
+ DOWNLOAD: 'download',
+}
+
+export const EXPENSE_ACTIONS = (Lng, language, imageUrl = '') => {
+
+
+ let viewReceipt = {
+ label: Lng.t("expenses.viewReceipt", { locale: language }),
+ value: ACTIONS_VALUE.DOWNLOAD
+ }
+
+ let actions = {
+ label: Lng.t("expenses.removeExpense", { locale: language }),
+ value: ACTIONS_VALUE.REMOVE
+ }
+
+
+ return imageUrl ? [
+ viewReceipt,
+ actions,
+ ] :
+ [actions]
+}
+
+
+// Endpoint Api URL
+// -----------------------------------------
+
+export const GET_EXPENSES_URL = (param) => `expenses?${queryString.stringify({
+ ...param,
+ orderByField: 'created_at',
+ orderBy: 'desc'
+})}`
+
+export const CREATE_EXPENSE_URL = () => `expenses`
+export const EDIT_EXPENSE_URL = (id) => `expenses/${id}`
+export const REMOVE_EXPENSE_URL = (id) => `expenses/${id}`
+
+export const GET_EDIT_EXPENSE_URL = (id) => `expenses/${id}/edit`
+export const GET_CREATE_EXPENSE_URL = () => `expenses/create`
+
+export const UPLOAD_RECEIPT_URL = (id) => `expenses/${id}/upload/receipts`
+export const SHOW_RECEIPT_URL = (id) => `expenses/${id}/show/receipt`
\ No newline at end of file
diff --git a/src/features/expenses/containers/Expense/index.js b/src/features/expenses/containers/Expense/index.js
new file mode 100644
index 00000000..3d0fbddd
--- /dev/null
+++ b/src/features/expenses/containers/Expense/index.js
@@ -0,0 +1,65 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { reduxForm, getFormValues } from 'redux-form';
+import moment from 'moment';
+import { Expense } from '../../components/Expense';
+import { validate } from './validation';
+import * as ExpensesAction from '../../actions';
+import { EXPENSE_FORM, EXPENSE_EDIT, EXPENSE_ADD } from '../../constants';
+
+const mapStateToProps = (state, { navigation }) => {
+
+ const {
+ global: { company, endpointURL, language },
+ expenses: { loading, categories, expense } = {}
+ } = state;
+
+ const type = navigation.getParam('type', EXPENSE_ADD)
+ const isLoading = loading.initExpenseLoading || (type === EXPENSE_EDIT && !expense)
+ || !categories || categories.length <= 0
+
+ return {
+ language,
+ categories,
+ company,
+ endpointURL,
+ initLoading: isLoading,
+ loading: loading.expenseLoading,
+ type,
+ formValues: getFormValues(EXPENSE_FORM)(state) || {},
+
+ initialValues: !isLoading && {
+ expense_date: moment(),
+ ...expense
+ }
+ };
+};
+
+const mapDispatchToProps = {
+ getCategories: ExpensesAction.getCategories,
+ createExpense: ExpensesAction.createExpense,
+ editExpense: ExpensesAction.editExpense,
+ getEditExpense: ExpensesAction.getEditExpense,
+ getCreateExpense: ExpensesAction.getCreateExpense,
+ clearExpense: ExpensesAction.clearExpense,
+ removeExpense: ExpensesAction.removeExpense,
+ getReceipt: ExpensesAction.getReceipt,
+};
+
+// Redux Forms
+const addExpenseReduxForm = reduxForm({
+ form: EXPENSE_FORM,
+ validate,
+})(Expense);
+
+// connect
+const ExpenseContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(addExpenseReduxForm);
+
+ExpenseContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default ExpenseContainer;
diff --git a/src/features/expenses/containers/Expense/validation.js b/src/features/expenses/containers/Expense/validation.js
new file mode 100644
index 00000000..7681e069
--- /dev/null
+++ b/src/features/expenses/containers/Expense/validation.js
@@ -0,0 +1,30 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const {
+ expense_date,
+ expense_category_id,
+ amount
+ } = values;
+
+ errors.expense_date = getError(
+ expense_date,
+ ['requiredField'],
+ );
+
+ errors.expense_category_id = getError(
+ expense_category_id,
+ ['requiredField'],
+ );
+
+ errors.amount = getError(
+ amount,
+ ['requiredField', 'isNumberFormat'],
+ );
+
+ return errors;
+};
diff --git a/src/features/expenses/containers/Expenses/index.js b/src/features/expenses/containers/Expenses/index.js
new file mode 100644
index 00000000..b9ece9a4
--- /dev/null
+++ b/src/features/expenses/containers/Expenses/index.js
@@ -0,0 +1,79 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { reduxForm, getFormValues } from 'redux-form';
+import * as ExpensesAction from '../../actions'
+import * as CategoriesAction from '../../../settings/actions';
+import { colors } from '../../../../styles/colors';
+import { Expenses } from '../../components/Expenses';
+import { EXPENSE_SEARCH } from '../../constants';
+import { EXPENSES } from '../../../../assets/svg';
+import { SvgXml } from 'react-native-svg';
+import { getTitleByLanguage, tabBarOnPress, navigateTabRoutes, navigateRoute } from '../../../../navigation/actions';
+import { ROUTES } from '../../../../navigation/routes';
+
+
+const mapStateToProps = (state) => {
+
+ const {
+ global: { language, currency },
+ expenses: {
+ expenses,
+ filterExpenses,
+ loading: { expensesLoading }
+ },
+ settings: { categories }
+ } = state;
+
+
+ return {
+ loading: expensesLoading,
+ expenses,
+ filterExpenses,
+ language,
+ currency,
+ categories,
+ formValues: getFormValues(EXPENSE_SEARCH)(state) || {},
+ };
+};
+
+const mapDispatchToProps = {
+ getExpenses: ExpensesAction.getExpenses,
+ getCategories: CategoriesAction.getExpenseCategories,
+};
+// Redux Forms
+const ExpensesSearchReduxForm = reduxForm({
+ form: EXPENSE_SEARCH
+})(Expenses);
+
+// connect
+const ExpensesContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(ExpensesSearchReduxForm);
+
+ExpensesContainer.navigationOptions = ({ navigation }) => ({
+
+ gesturesEnabled: false,
+ tabBarLabel: getTitleByLanguage('tabNavigation.expenses'),
+ tabBarIcon: ({ focused }: { focused: boolean }) => (
+
+ ),
+ tabBarOnPress: () => {
+
+ navigateTabRoutes(ROUTES.MAIN_EXPENSES, { apiCall: false })
+
+ let apiCall = navigation.getParam('apiCall', false)
+
+ apiCall ? navigateRoute(ROUTES.MAIN_EXPENSES) : tabBarOnPress(
+ ROUTES.MAIN_EXPENSES,
+ ExpensesAction.getExpenses
+ )
+ }
+});
+
+export default ExpensesContainer;
diff --git a/src/features/expenses/reducers/index.js b/src/features/expenses/reducers/index.js
new file mode 100644
index 00000000..7d3a7476
--- /dev/null
+++ b/src/features/expenses/reducers/index.js
@@ -0,0 +1,71 @@
+import {
+ SET_CATEGORIES,
+ EXPENSES_TRIGGER_SPINNER,
+ SET_EXPENSES,
+ CREATE_EXPENSE,
+ SET_EXPENSE,
+ CLEAR_EXPENSE,
+ SET_FILTER_EXPENSES
+} from "../constants";
+
+const initialState = {
+ expenses: [],
+ filterExpenses: [],
+ categories: [],
+ errors: null,
+ expense: null,
+ customers: null,
+ loading: {
+ expensesLoading: false,
+ expenseLoading: false,
+ initExpenseLoading: false,
+ categoriesLoading: false,
+ },
+};
+
+export default function expensesReducer(state = initialState, action) {
+ const { payload, type } = action;
+
+ switch (type) {
+
+ case SET_EXPENSES:
+
+ let { expenses, fresh } = payload;
+
+ if (!fresh) {
+ return { ...state, expenses: [...state.expenses, ...expenses] };
+ }
+
+ return { ...state, expenses };
+
+ case SET_FILTER_EXPENSES:
+
+ if (!payload.fresh) {
+ return {
+ ...state,
+ filterExpenses: [...state.filterExpenses, ...payload.expenses]
+ };
+ }
+
+ return { ...state, filterExpenses: payload.expenses };
+
+ case SET_CATEGORIES:
+ return { ...state, categories: payload.categories };
+
+ case EXPENSES_TRIGGER_SPINNER:
+ return { ...state, loading: { ...state.loading, ...payload } };
+
+ case CREATE_EXPENSE:
+ return { ...state, ...payload };
+
+ case CLEAR_EXPENSE:
+ return { ...state, expense: null };
+
+ case SET_EXPENSE:
+ const { expense, customers, categories } = payload
+ return { ...state, expense, customers, categories };
+
+ default:
+ return state;
+ }
+}
diff --git a/src/features/expenses/saga/index.js b/src/features/expenses/saga/index.js
new file mode 100644
index 00000000..9e9f7039
--- /dev/null
+++ b/src/features/expenses/saga/index.js
@@ -0,0 +1,266 @@
+import { call, put, takeEvery } from 'redux-saga/effects';
+import Request from '../../../api/request';
+
+import {
+ GET_EXPENSES,
+ CREATE_EXPENSE,
+ GET_EDIT_EXPENSE,
+ GET_CREATE_EXPENSE,
+ EDIT_EXPENSE,
+ REMOVE_EXPENSE,
+ GET_RECEIPT,
+ DOWNLOAD_RECEIPT,
+ // Endpoint Api URL
+ GET_EXPENSES_URL,
+ GET_CREATE_EXPENSE_URL,
+ CREATE_EXPENSE_URL,
+ EDIT_EXPENSE_URL,
+ GET_EDIT_EXPENSE_URL,
+ REMOVE_EXPENSE_URL,
+ UPLOAD_RECEIPT_URL,
+ SHOW_RECEIPT_URL,
+} from '../constants';
+import {
+ expenseTriggerSpinner,
+ setCategories,
+ setExpenses,
+ setExpense,
+ setFilterExpenses,
+} from '../actions';
+import { ROUTES } from '../../../navigation/routes';
+import { store } from '../../../store';
+
+function* getExpenses(payloadData) {
+ const {
+ payload: {
+ onResult = null,
+ onMeta = null,
+ fresh = true,
+ filter = false,
+ params = null,
+ pagination: { page = 1, limit = 10 } = {},
+ } = {},
+ } = payloadData;
+
+ yield put(expenseTriggerSpinner({ expensesLoading: true }));
+
+ try {
+
+ let param = {
+ ...params,
+ page,
+ limit
+ }
+
+ const options = {
+ path: GET_EXPENSES_URL(param),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ if (!filter)
+ yield put(setExpenses({ expenses: response.expenses.data, fresh }));
+ else
+ yield put(setFilterExpenses({ expenses: response.expenses.data, fresh }));
+
+ onMeta && onMeta(response.expenses);
+
+ onResult && onResult(true);
+ } catch (error) {
+ // console.log(error);
+ onResult && onResult(false);
+ } finally {
+ yield put(expenseTriggerSpinner({ expensesLoading: false }));
+ }
+}
+
+
+function* getCreateExpense(payloadData) {
+
+ const {
+ payload: { onResult } = {}
+ } = payloadData;
+
+ yield put(expenseTriggerSpinner({ initExpenseLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_CREATE_EXPENSE_URL(),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setCategories(response));
+
+ onResult && onResult(response)
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(expenseTriggerSpinner({ initExpenseLoading: false }));
+ }
+}
+
+function* createExpense(payloadData) {
+ const {
+ payload: { params, navigation, attachmentReceipt, onResult },
+ } = payloadData;
+ yield put(expenseTriggerSpinner({ expenseLoading: true }));
+
+ try {
+
+
+ const options = {
+ path: CREATE_EXPENSE_URL(),
+ body: params,
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ if (attachmentReceipt) {
+ const options2 = {
+ path: UPLOAD_RECEIPT_URL(response.expense.id),
+ image: attachmentReceipt,
+ type: 'create',
+ imageName: 'attachment_receipt'
+ }
+
+ yield call([Request, 'post'], options2);
+ }
+
+ onResult && onResult(response)
+ yield call(getExpenses, payload = {});
+
+ } catch (error) {
+ // console.log(error)
+ } finally {
+ yield put(expenseTriggerSpinner({ expenseLoading: false }));
+ }
+}
+
+function* editExpense(payloadData) {
+ const {
+ payload: { params, id, attachmentReceipt, onResult },
+ } = payloadData;
+ yield put(expenseTriggerSpinner({ expenseLoading: true }));
+
+ try {
+
+
+ const options = {
+ path: EDIT_EXPENSE_URL(id),
+ body: params,
+ };
+
+ const response = yield call([Request, 'put'], options);
+
+ if (attachmentReceipt) {
+ const options2 = {
+ path: UPLOAD_RECEIPT_URL(id),
+ image: attachmentReceipt,
+ type: 'edit',
+ imageName: 'attachment_receipt'
+ }
+
+ yield call([Request, 'post'], options2);
+ }
+
+
+ onResult && onResult(response)
+ yield call(getExpenses, payload = {});
+
+ } catch (error) {
+ // console.log(error)
+ } finally {
+ yield put(expenseTriggerSpinner({ expenseLoading: false }));
+ }
+}
+
+function* getEditExpense(payloadData) {
+ const {
+ payload: { id, onResult },
+ } = payloadData;
+ yield put(expenseTriggerSpinner({ initExpenseLoading: true }));
+
+ try {
+
+
+ const options = {
+ path: GET_EDIT_EXPENSE_URL(id),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setExpense(response))
+
+ onResult && onResult(response.expense)
+
+ } catch (error) {
+ // console.log(error)
+ } finally {
+ yield put(expenseTriggerSpinner({ initExpenseLoading: false }));
+ }
+}
+
+function* removeExpense(payloadData) {
+ const {
+ payload: { id, navigation },
+ } = payloadData;
+
+ yield put(expenseTriggerSpinner({ expenseLoading: true }));
+
+ try {
+
+
+ const options = {
+ path: REMOVE_EXPENSE_URL(id),
+ };
+
+ const response = yield call([Request, 'delete'], options);
+
+ if (response.success) {
+ navigation.navigate(ROUTES.MAIN_EXPENSES)
+ yield call(getExpenses, payload = {});
+ }
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(expenseTriggerSpinner({ expenseLoading: false }));
+ }
+}
+
+function* getReceipt(payloadData) {
+ const {
+ payload: { id, onResult },
+ } = payloadData;
+
+ yield put(expenseTriggerSpinner({ initExpenseLoading: true }));
+
+ try {
+
+
+ const options = {
+ path: SHOW_RECEIPT_URL(id),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ onResult && onResult(response)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(expenseTriggerSpinner({ initExpenseLoading: false }));
+ }
+}
+
+export default function* expensesSaga() {
+ yield takeEvery(GET_EXPENSES, getExpenses);
+ yield takeEvery(GET_CREATE_EXPENSE, getCreateExpense);
+ yield takeEvery(CREATE_EXPENSE, createExpense);
+ yield takeEvery(GET_EDIT_EXPENSE, getEditExpense);
+ yield takeEvery(EDIT_EXPENSE, editExpense);
+ yield takeEvery(REMOVE_EXPENSE, removeExpense);
+ yield takeEvery(GET_RECEIPT, getReceipt);
+}
diff --git a/src/features/invoices/actions/index.js b/src/features/invoices/actions/index.js
new file mode 100644
index 00000000..264b527e
--- /dev/null
+++ b/src/features/invoices/actions/index.js
@@ -0,0 +1,146 @@
+import {
+ GET_INVOICES,
+ SET_INVOICES,
+ CLEAR_INVOICES,
+ GET_CREATE_INVOICE,
+ SET_CREATE_INVOICE,
+ INVOICES_TRIGGER_SPINNER,
+ ADD_ITEM,
+ GET_ITEMS,
+ SET_ITEMS,
+ SET_INVOICE_ITEMS,
+ CREATE_INVOICE,
+ EDIT_ITEM,
+ SET_EDIT_INVOICE_ITEMS,
+ REMOVE_ITEM,
+ REMOVE_INVOICE_ITEM,
+ GET_EDIT_INVOICE,
+ SET_EDIT_INVOICE,
+ EDIT_INVOICE,
+ REMOVE_INVOICE_ITEMS,
+ CLEAR_INVOICE,
+ SET_INVOICE,
+ REMOVE_INVOICE,
+ REMOVE_FROM_INVOICES,
+ CHANGE_INVOICE_STATUS,
+ SET_ACTIVE_TAB,
+} from "../constants";
+
+export const getInvoices = (payload = {}) => ({
+ type: GET_INVOICES,
+ payload,
+});
+
+export const setInvoices = (payload = {}) => ({
+ type: SET_INVOICES,
+ payload,
+});
+
+export const clearInvoices = (payload = {}) => ({
+ type: CLEAR_INVOICES,
+ payload,
+});
+
+export const clearInvoice = (payload = {}) => ({
+ type: CLEAR_INVOICE,
+ payload,
+});
+
+export const getCreateInvoice = (payload = {}) => ({
+ type: GET_CREATE_INVOICE,
+ payload,
+});
+
+export const getEditInvoice = (payload = {}) => ({
+ type: GET_EDIT_INVOICE,
+ payload,
+});
+
+export const createInvoice = (payload = {}) => ({
+ type: CREATE_INVOICE,
+ payload,
+});
+
+export const editInvoice = (payload = {}) => ({
+ type: EDIT_INVOICE,
+ payload,
+});
+
+export const setInvoice = (payload = {}) => ({
+ type: SET_INVOICE,
+ payload,
+});
+
+export const setEditInvoice = (payload = {}) => ({
+ type: SET_EDIT_INVOICE,
+ payload,
+});
+
+export const invoiceTriggerSpinner = (payload) => ({
+ type: INVOICES_TRIGGER_SPINNER,
+ payload,
+});
+
+export const addItem = (payload = {}) => ({
+ type: ADD_ITEM,
+ payload,
+});
+
+export const getItems = (payload = {}) => ({
+ type: GET_ITEMS,
+ payload,
+});
+
+export const setItems = (payload = {}) => ({
+ type: SET_ITEMS,
+ payload,
+});
+
+export const setInvoiceItems = (payload = {}) => ({
+ type: SET_INVOICE_ITEMS,
+ payload,
+});
+
+export const editItem = (payload = {}) => ({
+ type: EDIT_ITEM,
+ payload,
+});
+
+export const setEditInvoiceItem = (payload = {}) => ({
+ type: SET_EDIT_INVOICE_ITEMS,
+ payload,
+});
+
+export const removeInvoice = (payload = {}) => ({
+ type: REMOVE_INVOICE,
+ payload,
+});
+
+export const removeFromInvoices = (payload = {}) => ({
+ type: REMOVE_FROM_INVOICES,
+ payload,
+});
+
+export const removeItem = (payload = {}) => ({
+ type: REMOVE_ITEM,
+ payload,
+});
+
+export const removeInvoiceItem = (payload = {}) => ({
+ type: REMOVE_INVOICE_ITEM,
+ payload,
+});
+
+export const removeInvoiceItems = () => ({
+ type: REMOVE_INVOICE_ITEMS
+});
+
+export const changeInvoiceStatus = (payload = {}) => ({
+ type: CHANGE_INVOICE_STATUS,
+ payload,
+});
+
+export const setInvoiceActiveTab = (payload = {}) => ({
+ type: SET_ACTIVE_TAB,
+ payload,
+});
\ No newline at end of file
diff --git a/src/features/invoices/components/Invoice/index.js b/src/features/invoices/components/Invoice/index.js
new file mode 100644
index 00000000..80c05356
--- /dev/null
+++ b/src/features/invoices/components/Invoice/index.js
@@ -0,0 +1,1032 @@
+// @flow
+
+import React from 'react';
+import {
+ View,
+ Text,
+ Alert,
+ Linking
+} from 'react-native';
+import { Field, change } from 'redux-form';
+import styles, { itemsDescriptionStyle } from './styles';
+import {
+ InputField,
+ DatePickerField,
+ CtDivider,
+ CtButton,
+ ListView,
+ DefaultLayout,
+ SelectField,
+ SelectPickerField,
+ CurrencyFormat,
+} from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import {
+ INVOICE_ADD,
+ INVOICE_EDIT,
+ ITEM_ADD,
+ ITEM_EDIT,
+ INVOICE_FORM,
+ ADD_INVOICE_ACTIONS,
+ INVOICE_ACTIONS,
+ EDIT_INVOICE_ACTIONS
+} from '../../constants';
+import { BUTTON_TYPE } from '../../../../api/consts/core';
+import { colors } from '../../../../styles/colors';
+import { TemplateField } from '../TemplateField';
+import { MOUNT, UNMOUNT, goBackWithFunction } from '../../../../navigation/actions';
+import Lng from '../../../../api/lang/i18n';
+import { INVOICE_DISCOUNT_OPTION } from '../../constants';
+import { CUSTOMER_ADD } from '../../../customers/constants';
+import { IMAGES } from '../../../../config';
+import { ADD_TAX } from '../../../settings/constants';
+import { PAYMENT_ADD } from '../../../payments/constants';
+import { MAX_LENGTH } from '../../../../api/global';
+
+
+type IProps = {
+ navigation: Object,
+ invoiceItems: Object,
+ taxTypes: Object,
+ customers: Object,
+ getCreateInvoice: Function,
+ getEditInvoice: Function,
+ clearInvoice: Function,
+ createInvoice: Function,
+ handleSubmit: Function,
+ getCustomers: Function,
+ getItems: Function,
+ editInvoice: Boolean,
+ itemsLoading: Boolean,
+ initLoading: Boolean,
+ loading: Boolean,
+ invoiceData: Object,
+ invoiceItems: Object,
+ items: Object,
+ language: String,
+ type: String
+
+}
+export class Invoice extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ taxTypeList: [],
+ currency: {},
+ itemList: [],
+ customerName: '',
+ markAsStatus: null,
+ };
+ }
+
+
+ componentDidMount() {
+
+ const {
+ getCreateInvoice,
+ navigation,
+ invoiceItems,
+ taxTypes,
+ getEditInvoice,
+ type,
+ } = this.props;
+
+ type === INVOICE_EDIT ?
+ getEditInvoice({
+ id: navigation.getParam('id'),
+ onResult: ({ user: { currency, name }, status }) => {
+ this.setState({
+ currency,
+ customerName: name,
+ markAsStatus: status
+ })
+ }
+ }) :
+ getCreateInvoice({
+ onResult: (val) => {
+ const { currency } = val
+
+ this.setState({ currency })
+ }
+ });
+
+ navigation.addListener(
+ 'didFocus',
+ payload => {
+ this.forceUpdate();
+ }
+ );
+
+ this.getInvoiceItemList(invoiceItems)
+
+ }
+
+ componentWillUnmount() {
+ const { clearInvoice } = this.props
+ clearInvoice();
+ goBackWithFunction(UNMOUNT)
+ }
+
+ androidBackHandler = () => {
+ const { navigation, handleSubmit } = this.props
+ goBackWithFunction(MOUNT, navigation, () => this.onDraft(handleSubmit))
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(INVOICE_FORM, field, value));
+ };
+
+ onEditItem = (item) => {
+ const {
+ navigation,
+ invoiceData: { discount_per_item, tax_per_item }
+ } = this.props
+ const { currency } = this.state
+
+ navigation.navigate(
+ ROUTES.INVOICE_ITEM,
+ { item, type: ITEM_EDIT, currency, discount_per_item, tax_per_item }
+ )
+ }
+
+ onDraft = (handleSubmit) => {
+ const { language, navigation, type } = this.props
+
+ if (type === INVOICE_EDIT) {
+ navigation.navigate(ROUTES.MAIN_INVOICES)
+ return
+ }
+ Alert.alert(
+ Lng.t("invoices.alert.draftTitle", { locale: language }),
+ '',
+ [
+ {
+ text: Lng.t("alert.action.saveAsDraft", { locale: language }),
+ onPress: handleSubmit(this.onSubmitInvoice)
+ },
+ {
+ text: Lng.t("alert.action.discard", { locale: language }),
+ onPress: () => {
+ navigation.navigate(ROUTES.MAIN_INVOICES)
+ },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ }
+
+ onSubmitInvoice = (values, status = 'draft') => {
+ const {
+ createInvoice,
+ navigation,
+ type,
+ editInvoice,
+ language,
+ } = this.props
+
+ if (this.finalAmount() < 0) {
+ alert(Lng.t("invoices.alert.lessAmount", { locale: language }))
+ return
+ }
+
+ let invoice = {
+ ...values,
+ total: this.finalAmount(),
+ sub_total: this.invoiceSubTotal(),
+ tax: this.invoiceTax() + this.invoiceCompoundTax(),
+ discount_val: this.totalDiscount(),
+ taxes: values.taxes ? values.taxes.map(val => {
+ return {
+ ...val,
+ amount: val.compound_tax ?
+ this.getCompoundTaxValue(val.percent) :
+ this.getTaxValue(val.percent),
+ }
+ }) : [],
+ }
+
+ if (status === 'send') {
+ invoice.invoiceSend = true
+ }
+
+ type === INVOICE_ADD ?
+ createInvoice({
+ invoice,
+ onResult: (url) => {
+ if (status === 'download') {
+ Linking.openURL(url);
+ }
+ navigation.navigate(ROUTES.MAIN_INVOICES)
+ }
+ }) :
+ editInvoice({
+ invoice: { ...invoice, id: navigation.getParam('id') },
+ onResult: (url) => {
+ if (status === 'download') {
+ Linking.openURL(url);
+ }
+ navigation.navigate(ROUTES.MAIN_INVOICES)
+ }
+ })
+ };
+
+ invoiceSubTotal = () => {
+ const { invoiceItems } = this.props
+ let subTotal = 0
+ invoiceItems.map(val => {
+ subTotal += JSON.parse(val.total)
+ })
+
+ return JSON.parse(subTotal)
+ }
+
+ subTotal = () => {
+ let invoiceTax = 0
+ this.invoiceItemTotalTaxes().filter(val => {
+ invoiceTax += val.amount
+ })
+ return (this.invoiceSubTotal() + invoiceTax) - this.totalDiscount()
+ }
+
+ invoiceTax = () => {
+ const { formValues: { taxes } } = this.props
+
+ let totalTax = 0
+
+ taxes && taxes.map(val => {
+ if (!val.compound_tax) {
+ totalTax += this.getTaxValue(val.percent)
+ }
+ })
+
+ return totalTax
+ }
+
+ invoiceCompoundTax = () => {
+ const { formValues: { taxes } } = this.props
+
+ let totalTax = 0
+
+ taxes && taxes.map(val => {
+ if (val.compound_tax) {
+ totalTax += this.getCompoundTaxValue(val.percent)
+ }
+ })
+
+ return totalTax
+ }
+
+ getTaxValue = (tax) => {
+ return (tax * JSON.parse(this.subTotal())) / 100
+ }
+
+ getCompoundTaxValue = (tax) => {
+ return (tax * JSON.parse(this.totalAmount())) / 100
+ }
+
+ getTaxName = (tax) => {
+ const { taxTypes } = this.props
+ let taxName = ''
+ const type = taxTypes.filter(val => val.fullItem.id === tax.tax_type_id)
+
+ if (type.length > 0) {
+ taxName = type[0]['fullItem'].name
+ }
+ return taxName
+ }
+
+ totalDiscount = () => {
+ const { formValues: { discount, discount_type } } = this.props
+
+ let discountPrice = 0
+
+ if (discount_type === 'percentage') {
+ discountPrice = ((discount * this.invoiceSubTotal()) / 100)
+ } else {
+ discountPrice = (discount * 100)
+ }
+
+ return discountPrice
+ }
+
+ totalAmount = () => {
+ return this.subTotal() + this.invoiceTax()
+ }
+
+ finalAmount = () => {
+ return this.totalAmount() + this.invoiceCompoundTax()
+ }
+
+ invoiceItemTotalTaxes = () => {
+ const { invoiceItems } = this.props
+ let taxes = []
+ invoiceItems.map(val => {
+ val.taxes && val.taxes.filter(tax => {
+ let hasSame = false
+ const { tax_type_id, id, amount } = tax
+
+ taxes = taxes.map(tax2 => {
+ if ((tax_type_id || id) === tax2.tax_type_id) {
+ hasSame = true
+ return {
+ ...tax2,
+ amount: amount + tax2.amount,
+ tax_type_id: tax2.tax_type_id
+ }
+ }
+ return tax2
+ })
+
+ if (!hasSame) {
+ taxes.push({ ...tax, tax_type_id: (tax_type_id || id) })
+ }
+ })
+ })
+ return taxes
+ }
+
+ FINAL_AMOUNT = () => {
+ const { currency } = this.state
+
+ const {
+ language,
+ taxTypes,
+ navigation,
+ invoiceData: { discount_per_item, tax_per_item },
+ formValues: { taxes }
+ } = this.props
+
+ let taxPerItem = !(tax_per_item === 'NO' || typeof tax_per_item === 'undefined' || tax_per_item === null)
+
+ let discountPerItem = !(discount_per_item === 'NO' || typeof discount_per_item === 'undefined' || discount_per_item === null)
+
+ return (
+
+
+
+
+ {Lng.t("invoices.subtotal", { locale: language })}
+
+
+
+
+
+
+
+ {(!discountPerItem) && (
+
+
+
+ {Lng.t("invoices.discount", { locale: language })}
+
+
+
+
+ {
+ this.setFormField('discount_type', val)
+ }}
+ isFakeInput
+ defaultPickerOptions={{
+ label: 'Fixed',
+ value: 'fixed',
+ color: colors.secondary,
+ displayLabel: currency ? currency.symbol : '$',
+ }}
+ fakeInputValueStyle={styles.fakeInputValueStyle}
+ fakeInputContainerStyle={styles.selectPickerField}
+ containerStyle={styles.SelectPickerContainer}
+ />
+
+
+ )}
+
+ {taxes &&
+ taxes.map((val, index) => !val.compound_tax ? (
+
+
+
+ {this.getTaxName(val)} ({val.percent} %)
+
+
+
+
+
+
+ ) : null
+ )
+ }
+
+ {taxes &&
+ taxes.map((val, index) => val.compound_tax ? (
+
+
+
+ {this.getTaxName(val)} ({val.percent} %)
+
+
+
+
+
+
+ ) : null
+ )
+ }
+
+ {this.DISPLAY_ITEM_TAX()}
+
+ {(!taxPerItem) && (
+
+ {Lng.t("invoices.taxPlaceholder", { locale: language })}
+
+ )
+ }}
+ navigation={navigation}
+ isMultiSelect
+ isInternalSearch
+ language={language}
+ concurrentMultiSelect
+ compareField="id"
+ valueCompareField="tax_type_id"
+ headerProps={{
+ title: Lng.t("taxes.title", { locale: language })
+ }}
+ rightIconPress={
+ () => navigation.navigate(ROUTES.TAX, {
+ type: ADD_TAX,
+ onSelect: (val) => {
+ this.setFormField('taxes',
+ [...val, ...taxes]
+ )
+ }
+ })
+ }
+ listViewProps={{
+ contentContainerStyle: { flex: 2 }
+ }}
+ emptyContentProps={{
+ contentType: "taxes",
+ }}
+ />
+ )}
+
+
+
+
+
+
+ {Lng.t("invoices.totalAmount", { locale: language })}:
+
+
+
+
+
+
+
+ )
+ };
+
+ BOTTOM_ACTION = () => {
+ const { language, loading, handleSubmit } = this.props
+
+ return (
+
+ this.onSubmitInvoice(val, status = INVOICE_ACTIONS.VIEW))}
+ btnTitle={Lng.t("button.viewPdf", { locale: language })}
+ type={BUTTON_TYPE.OUTLINE}
+ containerStyle={styles.handleBtn}
+ buttonContainerStyle={styles.buttonContainer}
+ loading={loading}
+ />
+
+ this.onSubmitInvoice(val, status = 'save'))}
+ btnTitle={Lng.t("button.save", { locale: language })}
+ containerStyle={styles.handleBtn}
+ buttonContainerStyle={styles.buttonContainer}
+ loading={loading}
+ />
+
+
+ )
+ }
+
+ DISPLAY_ITEM_TAX = () => {
+ const { currency } = this.state
+ let taxes = this.invoiceItemTotalTaxes()
+
+ return taxes ? (
+ taxes.map((val, index) => (
+
+
+
+ {this.getTaxName(val)} ({val.percent} %)
+
+
+
+
+
+
+ )
+ )
+ ) : null
+ }
+
+ getInvoiceItemList = (invoiceItems) => {
+ this.setFormField('items', invoiceItems)
+
+ const { currency } = this.state
+
+ let invoiceItemList = []
+
+ if (typeof invoiceItems !== 'undefined' && invoiceItems.length != 0) {
+
+ invoiceItemList = invoiceItems.map((item) => {
+
+ let { name, description, price, quantity, total } = item
+
+ return {
+ title: name,
+ subtitle: {
+ title: description,
+ labelComponent: (
+
+ ),
+ },
+ amount: total,
+ currency,
+ fullItem: item,
+ };
+ });
+ }
+
+ return invoiceItemList
+ }
+
+ getItemList = (items) => {
+ const { currency } = this.state
+
+ let itemList = []
+
+ if (typeof items !== 'undefined' && items.length != 0) {
+
+ itemList = items.map((item) => {
+
+ let { name, description, price } = item
+
+ return {
+ title: name,
+ subtitle: {
+ title: description,
+ },
+ amount: price,
+ currency,
+ fullItem: item,
+ };
+ });
+ }
+
+ return itemList
+ }
+
+ onOptionSelect = (action) => {
+ const {
+ removeInvoice,
+ navigation,
+ language,
+ formValues: {
+ user,
+ user_id,
+ due_amount,
+ invoice_number,
+ id,
+ },
+ handleSubmit,
+ changeInvoiceStatus,
+ type
+ } = this.props
+
+ switch (action) {
+
+ case INVOICE_ACTIONS.SEND:
+
+ type === INVOICE_EDIT ? changeInvoiceStatus({
+ id: navigation.getParam('id'),
+ action: 'send',
+ navigation
+ }) :
+ handleSubmit((val) => this.onSubmitInvoice(val, action))()
+
+ break;
+
+ case INVOICE_ACTIONS.MARK_AS_SENT:
+ changeInvoiceStatus({
+ id: navigation.getParam('id'),
+ action: 'mark-as-sent',
+ navigation
+ })
+ break;
+
+ case INVOICE_ACTIONS.RECORD_PAYMENT:
+
+ let invoice = {
+ user,
+ user_id,
+ due_amount,
+ invoice_number,
+ id
+ }
+
+ navigation.navigate(ROUTES.PAYMENT,
+ { type: PAYMENT_ADD, invoice, hasRecordPayment: true }
+ )
+ break;
+
+ case INVOICE_ACTIONS.DELETE:
+ Alert.alert(
+ Lng.t("alert.title", { locale: language }),
+ Lng.t("invoices.alert.removeDescription", { locale: language }),
+ [
+ {
+ text: 'OK',
+ onPress: () => {
+ removeInvoice({
+ id: navigation.getParam('id'),
+ onResult: (res) => {
+ res.success &&
+ navigation.navigate(ROUTES.MAIN_INVOICES)
+
+ res.error && (res.error === 'payment_attached') &&
+ Alert.alert(
+ Lng.t("invoices.alert.paymentAttachedTitle", { locale: language }),
+ Lng.t("invoices.alert.paymentAttachedDescription", { locale: language }),
+ [
+ {
+ text: 'OK',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ }
+ })
+ }
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ handleInvoiceAction = (action, title) => {
+ const { handleSubmit } = this.props
+
+ Alert.alert(
+ title,
+ '',
+ [
+ {
+ text: 'OK',
+ onPress: handleSubmit((val) => this.onSubmitInvoice(val, action))
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ }
+
+ render() {
+ const {
+ navigation,
+ handleSubmit,
+ invoiceData: {
+ invoiceTemplates,
+ discount_per_item,
+ tax_per_item
+ } = {},
+ invoiceItems,
+ getItems,
+ itemsLoading,
+ items,
+ language,
+ initLoading,
+ type,
+ getCustomers,
+ customers,
+ customersLoading,
+ } = this.props;
+
+ const { currency, customerName, markAsStatus } = this.state
+
+ const isEditInvoice = (type === INVOICE_EDIT)
+
+ !initLoading && this.androidBackHandler()
+
+ let hasSentStatus = (markAsStatus === 'SENT')
+ let hasCompleteStatus = (markAsStatus === 'COMPLETED')
+
+ let drownDownProps = !initLoading ? {
+ options: isEditInvoice ?
+ EDIT_INVOICE_ACTIONS(language, hasSentStatus, hasCompleteStatus) :
+ ADD_INVOICE_ACTIONS(language),
+ onSelect: this.onOptionSelect,
+ cancelButtonIndex: isEditInvoice ?
+ hasSentStatus ? 2 :
+ hasCompleteStatus ? 1 : 4
+ : 1,
+ destructiveButtonIndex: isEditInvoice ?
+ hasSentStatus ? 1 :
+ hasCompleteStatus ? 2 : 3
+ : 2,
+ } : null
+
+
+ return (
+ this.onDraft(handleSubmit),
+ title: isEditInvoice ?
+ Lng.t("header.editInvoice", { locale: language }) :
+ Lng.t("header.addInvoice", { locale: language }),
+ placement: "center",
+ }}
+
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ loadingProps={{ is: initLoading }}
+ dropdownProps={drownDownProps}
+ >
+
+
+
+
+ this.setFormField('invoice_date', val)
+ }
+ />
+
+
+
+ this.setFormField('due_date', val)
+ }
+ />
+
+
+
+
+
+ {
+ this.setFormField('user_id', item.id)
+ this.setState({ currency: item.currency })
+ }}
+ rightIconPress={
+ () => navigation.navigate(ROUTES.CUSTOMER, {
+ type: CUSTOMER_ADD,
+ currency,
+ onSelect: (val) => {
+ this.setFormField('user_id', val.id)
+ this.setState({ currency: val.currency })
+ }
+ })
+ }
+ headerProps={{
+ title: Lng.t("customers.title", { locale: language }),
+ }}
+ listViewProps={{
+ hasAvatar: true,
+ }}
+ emptyContentProps={{
+ contentType: "customers",
+ image: IMAGES.EMPTY_CUSTOMERS,
+ }}
+ fakeInputProps={{ loading: customersLoading }}
+ />
+
+
+ {Lng.t("invoices.items", { locale: language })}
+ *
+
+
+
+
+ {
+ navigation.navigate(ROUTES.INVOICE_ITEM, {
+ item,
+ currency,
+ type: ITEM_ADD,
+ discount_per_item,
+ tax_per_item
+ })
+ }
+ }
+ rightIconPress={
+ () => navigation.navigate(ROUTES.INVOICE_ITEM, {
+ type: ITEM_ADD,
+ currency,
+ discount_per_item,
+ tax_per_item
+ })
+ }
+ headerProps={{
+ title: Lng.t("items.title", { locale: language }),
+ }}
+ emptyContentProps={{
+ contentType: "items",
+ image: IMAGES.EMPTY_ITEMS,
+ }}
+ listViewProps={{
+ leftSubTitleStyle: itemsDescriptionStyle()
+ }}
+ />
+
+ {this.FINAL_AMOUNT(invoiceItems)}
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
diff --git a/src/features/invoices/components/Invoice/styles.js b/src/features/invoices/components/Invoice/styles.js
new file mode 100644
index 00000000..60fd0977
--- /dev/null
+++ b/src/features/invoices/components/Invoice/styles.js
@@ -0,0 +1,182 @@
+import { StyleSheet, Platform, Dimensions } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+import { SymbolStyle } from '../../../../components/CurrencyFormat/styles';
+
+const { width } = Dimensions.get('window');
+
+export const itemsDescriptionStyle = (widthMinus = 43) => {
+ return {
+ width: width - widthMinus,
+ textAlign: "justify"
+ }
+}
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerTitle: {
+ fontSize: 17,
+ color: colors.dark1,
+ },
+ headerContainer: {
+ // backgroundColor: colors.veryLightGray,
+ },
+ discount: {
+ marginTop: 10,
+ },
+ required: {
+ color: colors.danger
+ },
+ selectPickerField: {
+ backgroundColor: colors.veryLightGray,
+ paddingRight: 15,
+ paddingLeft: 5,
+ ...Platform.select({
+ ios: {
+ paddingRight: 30,
+ },
+ }),
+ borderLeftWidth: 0,
+ },
+ fieldStyle: {
+ display: 'flex',
+ minWidth: 80,
+ marginTop: -6,
+ },
+ discountField: {
+ display: 'flex',
+ flexDirection: 'row'
+ },
+ taxFakeInput: {
+ color: colors.primary,
+ textAlign: 'right',
+ fontFamily: fonts.poppinsMedium,
+ fontSize: 16,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ dateFieldContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ marginHorizontal: -10,
+ },
+ dateField: {
+ flex: 1,
+ paddingHorizontal: 7,
+ justifyContent: 'space-between',
+ },
+ inputFieldStyle: {
+ borderRadius: 0,
+ },
+ inputFieldContainer: {
+ paddingHorizontal: 0,
+ },
+ inputTextStyle: {
+ color: colors.dark3,
+ fontFamily: fonts.poppinsMedium,
+ },
+ inputFieldValidation: {
+ marginHorizontal: 0,
+ },
+ hintStyle: {
+ marginTop: -6,
+ paddingBottom: 6,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+ noteHintStyle: {
+ paddingBottom: 6,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+
+ amountContainer: {
+ borderWidth: 0.8,
+ borderColor: colors.lightGray,
+ marginTop: 24,
+ marginBottom: 10,
+ padding: 20,
+ backgroundColor: colors.white,
+ },
+ subContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ amountHeading: {
+ color: colors.darkGray,
+ fontFamily: fonts.poppinsMedium,
+ marginTop: 6,
+ },
+ subAmount: {
+ color: colors.dark2,
+ fontSize: 16,
+ },
+ finalAmount: {
+ color: colors.primary,
+ fontSize: 18,
+ fontWeight: '500',
+ fontFamily: fonts.poppinsMedium,
+ },
+ divider: {
+ backgroundColor: colors.lightGray,
+ borderColor: colors.lightGray,
+ borderWidth: 0.7,
+ marginTop: 10,
+ marginBottom: 8,
+ },
+ submitButton: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ },
+ handleBtn: {
+ marginHorizontal: 9,
+ },
+ buttonContainer: {
+ flex: 1,
+ },
+ itemContainer: {
+ marginVertical: 4,
+ borderWidth: 1,
+ borderColor: colors.lightGray
+ },
+ itemLeftTitle: {
+ fontSize: 15,
+ fontFamily: fonts.poppins,
+ color: colors.dark
+ },
+ itemLeftSubTitleLabel: {
+ marginLeft: -6,
+ },
+ itemLeftSubTitle: {
+ color: colors.darkGray,
+ fontSize: 13,
+ },
+ itemRightTitle: {
+ fontFamily: fonts.poppins,
+ fontSize: 18,
+ color: colors.secondary
+ },
+ label: {
+ paddingBottom: 4,
+ paddingTop: 12,
+ },
+ SelectPickerContainer: {
+ marginTop: 0
+ },
+ fakeInputValueStyle: {
+ fontSize: 18,
+ ...Platform.select({
+ android: {
+ marginTop: -4,
+ },
+ }),
+ ...SymbolStyle
+ },
+});
diff --git a/src/features/invoices/components/Invoices/index.js b/src/features/invoices/components/Invoices/index.js
new file mode 100644
index 00000000..a012278b
--- /dev/null
+++ b/src/features/invoices/components/Invoices/index.js
@@ -0,0 +1,501 @@
+// @flow
+
+import React from 'react';
+import { View, Text } from 'react-native';
+import { change } from 'redux-form';
+import styles from './styles';
+import { Tabs, MainLayout } from '../../../../components';
+import Due from '../Tab/Due';
+import Draft from '../Tab/Draft';
+import All from '../Tab/All';
+
+import { ROUTES } from '../../../../navigation/routes';
+import {
+ INVOICES_TABS,
+ INVOICE_ADD,
+ INVOICE_EDIT,
+ INVOICE_SEARCH,
+ FILTER_INVOICE_STATUS,
+ TAB_NAME,
+ FILTER_INVOICE_PAID_STATUS,
+} from '../../constants';
+import Lng from '../../../../api/lang/i18n';
+import { IMAGES } from '../../../../config';
+import moment from 'moment';
+
+let params = {
+ search: '',
+ customer_id: '',
+ invoice_number: '',
+ from_date: '',
+ to_date: '',
+}
+
+type IProps = {
+ language: String,
+ navigation: Object,
+ invoices: Object,
+ customers: Object,
+ loading: Boolean,
+ handleSubmit: Function,
+ getCustomers: Function,
+}
+
+export class Invoices extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ activeTab: INVOICES_TABS.DUE,
+ refreshing: false,
+ fresh: true,
+ pagination: {
+ page: 1,
+ limit: 10,
+ lastPage: 1,
+ },
+ search: '',
+ filter: false,
+ selectedFromDate: '',
+ selectedToDate: '',
+ selectedFromDateValue: '',
+ selectedToDateValue: ''
+ };
+ }
+
+ componentWillUpdate(nextProps, nextState) {
+
+ const { navigation } = nextProps
+ const pagination = navigation.getParam('pagination', null)
+ const apiCall = navigation.getParam('apiCall', false)
+
+ if (pagination && !(apiCall)) {
+
+ navigation.setParams({ 'pagination': null, apiCall: true })
+
+ const { last_page, current_page } = pagination
+ this.setState({
+ pagination: {
+ ...this.state.pagination,
+ lastPage: last_page,
+ page: current_page + 1,
+ }
+ });
+ }
+ }
+
+ setActiveTab = (activeTab) => {
+ const { refreshing, search } = this.state;
+
+ this.setState({ filter: false })
+
+ if (!refreshing) {
+ let type = this.getActiveTab(activeTab)
+
+ this.getItems({ fresh: true, type, q: search });
+
+ this.setState({ activeTab });
+
+ this.props.setInvoiceActiveTab({ activeTab: type })
+ }
+ };
+
+ getItems = ({
+ fresh = false,
+ onResult,
+ type,
+ params,
+ q = '',
+ resetFilter = false
+ } = {}) => {
+ const { getInvoices } = this.props;
+ const { refreshing, pagination } = this.state;
+
+ if (refreshing) {
+ return;
+ }
+
+ if (resetFilter)
+ this.setState({ filter: false })
+
+ this.setState({
+ refreshing: true,
+ fresh,
+ });
+
+ const paginationParams = fresh ? { ...pagination, page: 1 } : pagination;
+
+ if (!fresh && paginationParams.lastPage < paginationParams.page) {
+ return;
+ }
+
+ getInvoices({
+ fresh,
+ type,
+ pagination: paginationParams,
+ params: { ...params, search: q },
+ onMeta: ({ last_page, current_page }) => {
+ this.setState({
+ pagination: {
+ ...paginationParams,
+ lastPage: last_page,
+ page: current_page + 1,
+ },
+ });
+ },
+ onResult: (val) => {
+ this.setState({
+ refreshing: false,
+ fresh: !val,
+ });
+ onResult && onResult();
+ },
+ });
+ };
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(INVOICE_SEARCH, field, value));
+ };
+
+ onInvoiceSelect = (invoice) => {
+ const { navigation } = this.props
+ this.setActiveTab(INVOICES_TABS.ALL)
+ this.onResetFilter(INVOICES_TABS.ALL)
+ navigation.navigate(ROUTES.INVOICE,
+ { id: invoice.id, type: INVOICE_EDIT }
+ )
+ };
+
+ onSearch = (search) => {
+ const type = this.getActiveTab()
+ this.setState({ search })
+ this.getItems({ fresh: true, type, q: search })
+ };
+
+ getActiveTab = (activeTab = this.state.activeTab) => {
+ let type = '';
+
+ if (activeTab == INVOICES_TABS.DUE) {
+ type = 'UNPAID';
+ } else if (activeTab == INVOICES_TABS.DRAFT) {
+ type = 'DRAFT';
+ }
+ return type
+ }
+
+
+ getFilterStatusType = (filterType) => {
+ let type = filterType
+ if (type === INVOICES_TABS.DUE)
+ type = 'UNPAID'
+ return type
+ }
+
+
+ onResetFilter = (tab = '') => {
+ const { filter } = this.state
+
+ this.setState({ filter: false })
+
+ if (filter && !tab) {
+ this.getItems({ fresh: true, q: '', type: this.getActiveTab() });
+ }
+ }
+
+ onSubmitFilter = ({ filterStatus = '', paid_status = '', from_date = '', to_date = '', invoice_number = '', customer_id = '' }) => {
+
+ if (filterStatus || paid_status || from_date || to_date || invoice_number || customer_id) {
+
+ if (filterStatus === INVOICES_TABS.DUE)
+ this.setState({ activeTab: INVOICES_TABS.DUE });
+ else if (filterStatus === INVOICES_TABS.DRAFT)
+ this.setState({ activeTab: INVOICES_TABS.DRAFT });
+ else
+ this.setState({ activeTab: INVOICES_TABS.ALL });
+
+ this.setState({ filter: true })
+
+
+ this.getItems({
+ fresh: true,
+ params: {
+ ...params,
+ customer_id,
+ invoice_number,
+ from_date,
+ to_date
+ },
+ type: filterStatus ? this.getFilterStatusType(filterStatus) : paid_status,
+ });
+
+ }
+ else
+ this.onResetFilter()
+ }
+
+ loadMoreItems = ({ type, q }) => {
+ const { filter } = this.state
+ const {
+ formValues: {
+ filterStatus = '',
+ paid_status = '',
+ from_date = '',
+ to_date = '',
+ invoice_number = '',
+ customer_id = ''
+ }
+ } = this.props
+
+ if (filter) {
+
+ this.getItems({
+ params: {
+ ...params,
+ customer_id,
+ invoice_number,
+ from_date,
+ to_date
+ },
+ type: filterStatus ? this.getFilterStatusType(filterStatus) : paid_status,
+ filter: true
+ })
+ }
+ else
+ this.getItems({ type, q });
+ }
+
+ onAddInvoice = () => {
+ const { navigation } = this.props
+ this.setActiveTab(INVOICES_TABS.ALL)
+ this.onResetFilter(INVOICES_TABS.ALL)
+ navigation.navigate(ROUTES.INVOICE, { type: INVOICE_ADD })
+ }
+
+ render() {
+ const {
+ language,
+ navigation,
+ invoices,
+ loading,
+ handleSubmit,
+ customers,
+ getCustomers,
+ } = this.props;
+
+ const {
+ activeTab,
+ refreshing,
+ pagination: { lastPage, page },
+ fresh,
+ search,
+ selectedFromDate,
+ selectedToDate,
+ selectedFromDateValue,
+ selectedToDateValue,
+ filter
+ } = this.state;
+
+ const canLoadMore = lastPage >= page;
+
+ let filterRefs = {}
+
+ let selectFields = [
+ {
+ name: "customer_id",
+ apiSearch: true,
+ hasPagination: true,
+ getItems: getCustomers,
+ items: customers,
+ displayName: "name",
+ label: Lng.t("invoices.customer", { locale: language }),
+ icon: 'user',
+ placeholder: Lng.t("customers.placeholder", { locale: language }),
+ navigation: navigation,
+ compareField: "id",
+ onSelect: (item) => this.setFormField('customer_id', item.id),
+ headerProps: {
+ title: Lng.t("customers.title", { locale: language }),
+ rightIconPress: null
+ },
+ listViewProps: {
+ hasAvatar: true,
+ },
+ emptyContentProps: {
+ contentType: "customers",
+ image: IMAGES.EMPTY_CUSTOMERS,
+ }
+ }
+ ]
+
+ let datePickerFields = [
+ {
+ name: "from_date",
+ label: Lng.t("invoices.fromDate", { locale: language }),
+ onChangeCallback: (formDate, displayDate) => {
+
+ this.setState({
+ selectedFromDate: displayDate,
+ selectedFromDateValue: formDate
+ })
+ },
+ selectedDate: selectedFromDate,
+ selectedDateValue: selectedFromDateValue
+
+ },
+ {
+ name: "to_date",
+ label: Lng.t("invoices.toDate", { locale: language }),
+ onChangeCallback: (formDate, displayDate) => {
+
+ this.setState({
+ selectedToDate: displayDate,
+ selectedToDateValue: formDate
+ })
+ },
+ selectedDate: selectedToDate,
+ selectedDateValue: selectedToDateValue
+ }
+ ]
+
+ let inputFields = [{
+ name: 'invoice_number',
+ hint: Lng.t("invoices.invoiceNumber", { locale: language }),
+ leftIcon: 'hashtag',
+ inputProps: {
+ autoCapitalize: 'none',
+ autoCorrect: true,
+ },
+ refLinkFn: (ref) => {
+ filterRefs.invNumber = ref;
+ }
+ }]
+
+ let dropdownFields = [
+ {
+ name: "filterStatus",
+ label: Lng.t("invoices.status", { locale: language }),
+ fieldIcon: 'align-center',
+ items: FILTER_INVOICE_STATUS,
+ onChangeCallback: (val) => {
+ this.setFormField('filterStatus', val)
+ },
+ defaultPickerOptions: {
+ label: Lng.t("invoices.statusPlaceholder", { locale: language }),
+ value: '',
+ },
+ containerStyle: styles.selectPicker
+ },
+ {
+ name: "paid_status",
+ label: Lng.t("invoices.paidStatus", { locale: language }),
+ fieldIcon: 'align-center',
+ items: FILTER_INVOICE_PAID_STATUS,
+ onChangeCallback: (val) => {
+ this.setFormField('paid_status', val)
+ },
+ defaultPickerOptions: {
+ label: Lng.t("invoices.paidStatusPlaceholder", { locale: language }),
+ value: '',
+ },
+ onDonePress: () => filterRefs.invNumber.focus(),
+ containerStyle: styles.selectPicker
+ }
+ ]
+
+ return (
+
+ {
+ this.onAddInvoice()
+ },
+ title: Lng.t("header.invoices", { locale: language }),
+ }}
+ onSearch={this.onSearch}
+ filter
+ filterProps={{
+ onSubmitFilter: handleSubmit(this.onSubmitFilter),
+ selectFields: selectFields,
+ datePickerFields: datePickerFields,
+ inputFields: inputFields,
+ dropdownFields: dropdownFields,
+ clearFilter: this.props,
+ language: language
+ }}
+ >
+
+ ),
+ },
+ {
+ Title: INVOICES_TABS.DRAFT,
+ tabName: TAB_NAME(INVOICES_TABS.DRAFT, language, Lng),
+ render: (
+
+ ),
+ },
+ {
+ Title: INVOICES_TABS.ALL,
+ tabName: TAB_NAME(INVOICES_TABS.ALL, language, Lng),
+ render: (
+
+ ),
+ },
+ ]}
+ />
+
+
+ );
+ }
+}
diff --git a/src/features/invoices/components/Invoices/styles.js b/src/features/invoices/components/Invoices/styles.js
new file mode 100644
index 00000000..78976214
--- /dev/null
+++ b/src/features/invoices/components/Invoices/styles.js
@@ -0,0 +1,18 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ Tabs: {
+ backgroundColor: colors.veryLightGray,
+ borderBottomRightRadius: 10,
+ borderBottomLeftRadius: 10,
+ },
+ selectPicker: {
+ marginTop: 12,
+ marginBottom: 2,
+ },
+});
diff --git a/src/features/invoices/components/Item/index.js b/src/features/invoices/components/Item/index.js
new file mode 100644
index 00000000..6f6f7c4e
--- /dev/null
+++ b/src/features/invoices/components/Item/index.js
@@ -0,0 +1,572 @@
+// @flow
+
+import React from 'react';
+import {
+ View,
+ Text,
+ ScrollView,
+ Alert,
+} from 'react-native';
+import { Field, change } from 'redux-form';
+import styles from './styles';
+import {
+ InputField,
+ CtDivider,
+ CtButton,
+ DefaultLayout,
+ SelectField,
+ SelectPickerField,
+ CurrencyFormat,
+ RadioButtonGroup,
+} from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import {
+ ITEM_DISCOUNT_OPTION,
+ ITEM_EDIT,
+ ITEM_ADD,
+ ITEM_FORM
+} from '../../constants';
+import { BUTTON_COLOR } from '../../../../api/consts/core';
+import { colors } from '../../../../styles/colors';
+import Lng from '../../../../api/lang/i18n';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { ADD_TAX } from '../../../settings/constants';
+import { MAX_LENGTH } from '../../../../api/global';
+import { ITEM_UNITS } from '../../../more/constants';
+
+export class InvoiceItem extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ taxTypeList: []
+ }
+ }
+
+ componentWillMount() {
+ const { taxTypes } = this.props;
+
+ this.setState({ taxTypeList: taxTypes })
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+
+ navigation.addListener(
+ 'didFocus',
+ payload => {
+ this.forceUpdate();
+ }
+ );
+
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(ITEM_FORM, field, value));
+ };
+
+ saveItem = (values) => {
+ const {
+ addItem,
+ removeInvoiceItem,
+ setInvoiceItems,
+ itemId,
+ navigation,
+ type,
+ language,
+ } = this.props
+
+ if (this.finalAmount() < 0) {
+ alert(Lng.t("items.lessAmount", { locale: language }))
+ return
+ }
+
+ const item = {
+ ...values,
+ final_total: this.finalAmount(),
+ total: this.subTotal(),
+ discount_val: this.totalDiscount(),
+ tax: this.itemTax() + this.itemCompoundTax(),
+ taxes: values.taxes && values.taxes.map(val => {
+ return {
+ ...val,
+ amount: val.compound_tax ?
+ this.getCompoundTaxValue(val.percent) :
+ this.getTaxValue(val.percent),
+ }
+ }),
+ }
+
+ const callback = () => {
+ addItem({
+ item,
+ onResult: () => {
+ navigation.goBack(null)
+ }
+ })
+ }
+
+ if (!itemId) {
+ callback()
+ } else {
+ const invoiceItem = [{ ...item, item_id: itemId }]
+
+ if (type === ITEM_EDIT) {
+ removeInvoiceItem({ id: itemId })
+ }
+
+ setInvoiceItems({ invoiceItem })
+
+ navigation.navigate(ROUTES.INVOICE)
+ }
+
+ };
+
+ removeItem = () => {
+ const { removeInvoiceItem, itemId, navigation, language } = this.props
+
+ Alert.alert(
+ Lng.t("alert.title", { locale: language }),
+ '',
+ [
+ {
+ text: 'OK',
+ onPress: () => {
+ navigation.navigate(ROUTES.INVOICE)
+ removeInvoiceItem({ id: itemId })
+ }
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+
+ }
+
+ totalDiscount = () => {
+ const { formValues: { discount, discount_type } } = this.props
+
+ let discountPrice = 0
+
+ if (discount_type === 'percentage') {
+ discountPrice = ((discount * this.itemSubTotal()) / 100)
+ } else if (discount_type === 'fixed') {
+ discountPrice = (discount * 100)
+ }
+ else if (discount_type === 'none') {
+ discountPrice = 0
+ this.setFormField('discount', 0)
+ }
+ return discountPrice
+ }
+
+
+ totalAmount = () => {
+ return this.subTotal() + this.itemTax()
+ }
+
+ itemSubTotal = () => {
+ const { formValues: { quantity, price } } = this.props
+
+ subTotal = (price * quantity)
+
+ return subTotal
+ }
+
+ subTotal = () => {
+ return this.itemSubTotal() - this.totalDiscount()
+ }
+
+ itemTax = () => {
+ const { formValues: { taxes } } = this.props
+
+ let totalTax = 0
+
+ taxes && taxes.map(val => {
+ if (!val.compound_tax) {
+ totalTax += this.getTaxValue(val.percent)
+ }
+ })
+
+ return totalTax
+ }
+
+ itemCompoundTax = () => {
+ const { formValues: { taxes } } = this.props
+
+ let totalTax = 0
+
+ taxes && taxes.map(val => {
+ if (val.compound_tax) {
+ totalTax += this.getCompoundTaxValue(val.percent)
+ }
+ })
+
+ return totalTax
+ }
+
+ getTaxValue = (tax) => {
+ return (tax * JSON.parse(this.subTotal())) / 100
+ }
+
+ getCompoundTaxValue = (tax) => {
+ return (tax * JSON.parse(this.totalAmount())) / 100
+ }
+
+ getTaxName = (tax) => {
+ const { taxTypes } = this.props
+ let taxName = ''
+
+ const type = taxTypes.filter(val => val.fullItem.id === tax.tax_type_id)
+
+ if (type.length > 0) {
+ taxName = type[0]['fullItem'].name
+ }
+ return taxName
+ }
+
+ finalAmount = () => {
+ return this.totalAmount() + this.itemCompoundTax()
+ }
+
+ FINAL_AMOUNT = () => {
+ const {
+ language,
+ formValues: { quantity, price, taxes },
+ navigation,
+ discountPerItem,
+ } = this.props
+
+ const currency = navigation.getParam('currency')
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {discountPerItem === 'YES' && (
+
+
+
+ {Lng.t("items.finalDiscount", { locale: language })}
+
+
+
+
+
+
+ )}
+
+ {taxes &&
+ taxes.map((val, index) => !val.compound_tax ? (
+
+
+
+ {val.name} ({val.percent} %)
+
+
+
+
+
+
+ ) : null)
+ }
+
+ {taxes &&
+ taxes.map(val => val.compound_tax ? (
+
+
+
+ {this.getTaxName(val)} ({val.percent} %)
+
+
+
+
+
+
+ ) : null)
+ }
+
+
+
+
+
+
+ {Lng.t("items.finalAmount", { locale: language })}
+
+
+
+
+
+
+
+ )
+ };
+
+ BOTTOM_ACTION = (handleSubmit) => {
+ const { language, loading, type } = this.props
+ const isCreateItem = (type === ITEM_ADD)
+ return (
+
+
+ {!isCreateItem && (
+
+ )}
+
+
+ )
+ }
+
+ render() {
+ const {
+ navigation,
+ handleSubmit,
+ loading,
+ formValues: { discount_type, taxes },
+ initialValues,
+ language,
+ type,
+ discountPerItem,
+ itemId,
+ taxTypes,
+ taxPerItem,
+ } = this.props;
+
+ const isCreateItem = (type === ITEM_ADD)
+ let itemRefs = {}
+
+ return (
+ navigation.navigate(ROUTES.INVOICE),
+ title: isCreateItem ?
+ Lng.t("header.addItem", { locale: language }) :
+ Lng.t("header.editItem", { locale: language }),
+ placement: "center",
+ rightIcon: 'save',
+ rightIconProps: {
+ solid: true
+ },
+ rightIconPress: handleSubmit(this.saveItem),
+ }}
+ loadingProps={{
+ is: loading
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ >
+
+ {
+ itemRefs.quantity.focus();
+ }
+ }}
+ />
+
+
+
+ {
+ itemRefs.price.focus();
+ }
+ }}
+ refLinkFn={(ref) => {
+ itemRefs.quantity = ref;
+ }}
+ />
+
+
+ {
+ itemRefs.price = ref;
+ }}
+ isCurrencyInput
+ />
+
+
+
+ {(initialValues.unit || !itemId) && (
+
+ )}
+
+ {discountPerItem == 'YES' && (
+
+
+
+
+
+ )}
+
+ {taxPerItem === 'YES' && (
+ navigation.navigate(ROUTES.TAX, {
+ type: ADD_TAX,
+ onSelect: (val) => {
+ this.setFormField('taxes',
+ [...val, ...taxes]
+ )
+ }
+ })
+ }
+ emptyContentProps={{
+ contentType: "taxes",
+ }}
+ />
+ )}
+
+ {this.FINAL_AMOUNT()}
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/invoices/components/Item/styles.js b/src/features/invoices/components/Item/styles.js
new file mode 100644
index 00000000..02abd95c
--- /dev/null
+++ b/src/features/invoices/components/Item/styles.js
@@ -0,0 +1,138 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+import { isIPhoneX } from '../../../../api/helper';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerTitle: {
+ fontSize: 17,
+ color: colors.dark1,
+ },
+ headerContainer: {
+ // backgroundColor: colors.veryLightGray,
+ },
+ taxFakeInput: {
+ color: colors.primary,
+ textAlign: 'right',
+ fontFamily: fonts.poppinsMedium,
+ fontSize: 16,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ dateFieldContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ marginHorizontal: -10
+ },
+ dateField: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingHorizontal: 10
+ },
+ inputFieldStyle: {
+ borderRadius: 0,
+ },
+ inputFieldContainer: {
+ paddingHorizontal: 0,
+ },
+ inputTextStyle: {
+ color: colors.dark3,
+ fontFamily: fonts.poppinsMedium,
+ },
+ inputFieldValidation: {
+ marginHorizontal: 0,
+ },
+ hintStyle: {
+ marginTop: -6,
+ paddingBottom: 6,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+ noteHintStyle: {
+ paddingBottom: 6,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+ fakeInputStyle: {
+ marginTop: 5,
+ },
+
+ amountContainer: {
+ borderWidth: 0.8,
+ borderColor: colors.lightGray,
+ marginTop: 24,
+ marginBottom: 18,
+ padding: 20,
+ backgroundColor: colors.white,
+ },
+ subContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ label: {
+ color: colors.gray,
+ fontFamily: fonts.poppinsMedium,
+ marginTop: 6,
+ },
+ price: {
+ color: colors.dark2,
+ fontSize: 16,
+ },
+ totalPrice: {
+ color: colors.primary,
+ fontSize: 18,
+ fontWeight: '500',
+ fontFamily: fonts.poppinsMedium,
+ },
+ divider: {
+ backgroundColor: colors.lightGray,
+ borderColor: colors.lightGray,
+ borderWidth: 0.7,
+ marginTop: 10,
+ marginBottom: 8,
+ },
+ submitButton: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ },
+ handleBtn: {
+ marginHorizontal: 9,
+ },
+ buttonContainer: {
+ flex: 1,
+ },
+ itemContainer: {
+ marginVertical: 4,
+ borderWidth: 1,
+ borderColor: colors.lightGray
+ },
+ itemLeftTitle: {
+ fontSize: 15,
+ fontFamily: fonts.poppins,
+ color: colors.dark
+ },
+ itemLeftSubTitleLabel: {
+ marginLeft: -6,
+ },
+ itemLeftSubTitle: {
+ color: colors.darkGray,
+ fontSize: 13,
+ },
+ itemRightTitle: {
+ fontFamily: fonts.poppins,
+ fontSize: 18,
+ color: colors.secondary
+ },
+ selectPicker: {
+ marginTop: 25,
+ },
+});
diff --git a/src/features/invoices/components/Tab/All.js b/src/features/invoices/components/Tab/All.js
new file mode 100644
index 00000000..74d58f8f
--- /dev/null
+++ b/src/features/invoices/components/Tab/All.js
@@ -0,0 +1,117 @@
+// @flow
+import React from 'react';
+import { View } from 'react-native';
+import { styles } from './styles';
+import { ListView, Content } from '../../../../components';
+import { IMAGES } from '../../../../config';
+import { INVOICES_STATUS_BG_COLOR, INVOICES_STATUS_TEXT_COLOR } from '../../constants';
+import Lng from '../../../../api/lang/i18n';
+
+type IProps = {
+ invoices: Array,
+ onInvoiceSelect: Function,
+ getInvoices: Function,
+ loading: String,
+ canLoadMore: Boolean,
+ refreshing: Boolean,
+ fresh: Boolean,
+ search: String,
+ loadMoreItems: Function,
+ onAddInvoice: Function,
+ filter: Boolean,
+};
+
+const All = ({
+ invoices,
+ onInvoiceSelect,
+ refreshing,
+ loading,
+ canLoadMore,
+ getInvoices,
+ fresh,
+ search,
+ language,
+ navigation,
+ loadMoreItems,
+ onAddInvoice,
+ filter
+}: IProps) => {
+ let items = [];
+
+ if (typeof invoices !== 'undefined' && invoices.length != 0) {
+ items = invoices.map((item) => {
+ const {
+ invoice_number,
+ user: { name, currency } = {},
+ status,
+ formattedDueDate,
+ total,
+ } = item;
+
+ return {
+ title: name,
+ subtitle: {
+ title: invoice_number,
+ label: status,
+ labelBgColor: INVOICES_STATUS_BG_COLOR[status],
+ labelTextColor: INVOICES_STATUS_TEXT_COLOR[status],
+ },
+ amount: total,
+ currency,
+ rightSubtitle: formattedDueDate,
+ fullItem: item,
+ };
+ });
+ }
+
+
+ let empty = (!filter && !search) ? {
+ description: Lng.t("invoices.empty.description", { locale: language }),
+ buttonTitle: Lng.t("invoices.empty.buttonTitle", { locale: language }),
+ buttonPress: () => onAddInvoice(),
+ } : {}
+
+ let emptyTitle = search ? Lng.t("search.noResult", { locale: language, search })
+ : (!filter) ? Lng.t("invoices.empty.all.title", { locale: language }) :
+ Lng.t("filter.empty.filterTitle", { locale: language })
+
+ let isLoading = navigation.getParam('loading', false)
+
+ return (
+
+
+ {
+ getInvoices({
+ fresh: true,
+ onResult: onHide,
+ type: '',
+ q: search,
+ resetFilter: true
+ });
+ }}
+ getItems={() => {
+ loadMoreItems({
+ type: '',
+ q: search,
+ });
+ }}
+ bottomDivider
+ emptyContentProps={{
+ title: emptyTitle,
+ image: IMAGES.EMPTY_INVOICES,
+ ...empty
+ }}
+ />
+
+
+ );
+};
+
+export default All;
diff --git a/src/features/invoices/components/Tab/Draft.js b/src/features/invoices/components/Tab/Draft.js
new file mode 100644
index 00000000..10ad3a83
--- /dev/null
+++ b/src/features/invoices/components/Tab/Draft.js
@@ -0,0 +1,117 @@
+// @flow
+import React from 'react';
+import { View } from 'react-native';
+import { styles } from './styles';
+import { ListView, Content } from '../../../../components';
+import { IMAGES } from '../../../../config';
+import Lng from '../../../../api/lang/i18n';
+import { INVOICES_STATUS_TEXT_COLOR, INVOICES_STATUS_BG_COLOR } from '../../constants';
+
+type IProps = {
+ invoices: Array,
+ onInvoiceSelect: Function,
+ getInvoices: Function,
+ loading: String,
+ canLoadMore: Boolean,
+ refreshing: Boolean,
+ fresh: Boolean,
+ search: String,
+ loadMoreItems: Function,
+ onAddInvoice: Function,
+ filter: Boolean
+};
+
+const Draft = ({
+ invoices,
+ onInvoiceSelect,
+ refreshing,
+ loading,
+ canLoadMore,
+ getInvoices,
+ fresh,
+ search,
+ language,
+ navigation,
+ loadMoreItems,
+ onAddInvoice,
+ filter
+}: IProps) => {
+ let items = [];
+
+ if (typeof invoices !== 'undefined' && invoices.length != 0) {
+ items = invoices.map((item) => {
+ const {
+ invoice_number,
+ user: { name, currency } = {},
+ status,
+ formattedDueDate,
+ total,
+ } = item;
+
+ return {
+ title: name,
+ subtitle: {
+ title: invoice_number,
+ label: status,
+ labelBgColor: INVOICES_STATUS_BG_COLOR[status],
+ labelTextColor: INVOICES_STATUS_TEXT_COLOR[status],
+ },
+ amount: total,
+ currency,
+ rightSubtitle: formattedDueDate,
+ fullItem: item,
+ };
+ });
+ }
+
+
+ let empty = (!filter && !search) ? {
+ description: Lng.t("invoices.empty.draft.description", { locale: language }),
+ buttonTitle: Lng.t("invoices.empty.buttonTitle", { locale: language }),
+ buttonPress: () => onAddInvoice(),
+ } : {}
+
+ let emptyTitle = search ? Lng.t("search.noResult", { locale: language, search })
+ : (!filter) ? Lng.t("invoices.empty.draft.title", { locale: language }) :
+ Lng.t("filter.empty.filterTitle", { locale: language })
+
+ let isLoading = navigation.getParam('loading', false)
+
+ return (
+
+
+ {
+ getInvoices({
+ fresh: true,
+ onResult: onHide,
+ type: 'DRAFT',
+ q: search,
+ resetFilter: true
+ });
+ }}
+ getItems={() => {
+ loadMoreItems({
+ type: 'DRAFT',
+ q: search,
+ });
+ }}
+ bottomDivider
+ emptyContentProps={{
+ title: emptyTitle,
+ image: IMAGES.EMPTY_INVOICES,
+ ...empty
+ }}
+ />
+
+
+ );
+};
+
+export default Draft;
diff --git a/src/features/invoices/components/Tab/Due.js b/src/features/invoices/components/Tab/Due.js
new file mode 100644
index 00000000..a43d999d
--- /dev/null
+++ b/src/features/invoices/components/Tab/Due.js
@@ -0,0 +1,116 @@
+// @flow
+import React from 'react';
+import { View } from 'react-native';
+import { styles } from './styles';
+import { ListView, Content } from '../../../../components';
+import { IMAGES } from '../../../../config';
+import Lng from '../../../../api/lang/i18n';
+import { INVOICES_STATUS_BG_COLOR, INVOICES_STATUS_TEXT_COLOR } from '../../constants';
+
+type IProps = {
+ invoices: Array,
+ onInvoiceSelect: Function,
+ getInvoices: Function,
+ loading: String,
+ canLoadMore: Boolean,
+ refreshing: Boolean,
+ fresh: Boolean,
+ search: String,
+ loadMoreItems: Function,
+ onAddInvoice: Function,
+ filter: Boolean
+};
+
+const Due = ({
+ invoices,
+ onInvoiceSelect,
+ refreshing,
+ loading,
+ canLoadMore,
+ getInvoices,
+ fresh,
+ search,
+ language,
+ navigation,
+ loadMoreItems,
+ onAddInvoice,
+ filter
+}: IProps) => {
+ let items = [];
+
+ if (typeof invoices !== 'undefined' && invoices.length != 0) {
+ items = invoices.map((item) => {
+ const {
+ invoice_number,
+ user: { name, currency } = {},
+ status,
+ formattedDueDate,
+ total,
+ } = item;
+
+ return {
+ title: name,
+ subtitle: {
+ title: invoice_number,
+ label: status,
+ labelBgColor: INVOICES_STATUS_BG_COLOR[status],
+ labelTextColor: INVOICES_STATUS_TEXT_COLOR[status],
+ },
+ amount: total,
+ currency,
+ rightSubtitle: formattedDueDate,
+ fullItem: item,
+ };
+ });
+ }
+
+ let empty = (!filter && !search) ? {
+ description: Lng.t("invoices.empty.due.description", { locale: language }),
+ buttonTitle: Lng.t("invoices.empty.buttonTitle", { locale: language }),
+ buttonPress: () => onAddInvoice(),
+ } : {}
+
+ let emptyTitle = search ? Lng.t("search.noResult", { locale: language, search })
+ : (!filter) ? Lng.t("invoices.empty.due.title", { locale: language }) :
+ Lng.t("filter.empty.filterTitle", { locale: language })
+
+ let isLoading = navigation.getParam('loading', false)
+
+ return (
+
+
+ {
+ getInvoices({
+ fresh: true,
+ onResult: onHide,
+ type: 'UNPAID',
+ q: search,
+ resetFilter: true
+ });
+ }}
+ getItems={() => {
+ loadMoreItems({
+ type: 'UNPAID',
+ q: search,
+ });
+ }}
+ bottomDivider
+ emptyContentProps={{
+ title: emptyTitle,
+ image: IMAGES.EMPTY_INVOICES,
+ ...empty
+ }}
+ />
+
+
+ );
+};
+
+export default Due;
diff --git a/src/features/invoices/components/Tab/styles.js b/src/features/invoices/components/Tab/styles.js
new file mode 100644
index 00000000..a72f6262
--- /dev/null
+++ b/src/features/invoices/components/Tab/styles.js
@@ -0,0 +1,7 @@
+import { StyleSheet } from 'react-native';
+
+export const styles = StyleSheet.create({
+ content: {
+ flex: 1,
+ },
+});
diff --git a/src/features/invoices/components/TemplateField/index.js b/src/features/invoices/components/TemplateField/index.js
new file mode 100644
index 00000000..1ad5705f
--- /dev/null
+++ b/src/features/invoices/components/TemplateField/index.js
@@ -0,0 +1,176 @@
+// @flow
+
+import React, { Component } from 'react';
+import {
+ View,
+} from 'react-native';
+import styles from './styles';
+import { SlideModal, FakeInput, AssetImage, CtButton } from '../../../../components';
+import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
+import { Icon } from 'react-native-elements';
+import { colors } from '../../../../styles/colors';
+import Lng from '../../../../api/lang/i18n';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { headerTitle } from '../../../../api/helper';
+
+type IProps = {
+ label: String,
+ icon: String,
+ onChangeCallback: Function,
+ placeholder: String,
+ containerStyle: Object,
+ rightIcon: String,
+ leftIcon: String,
+ color: String,
+ value: String,
+ templates: Array,
+};
+
+export class TemplateField extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ page: 1,
+ visible: false,
+ selectedTemplate: '',
+ };
+ }
+
+ componentDidMount() {
+ const { input: { value }, templates, navigation } = this.props
+
+ const template = templates.filter(val => val.id === value)[0]
+
+ this.setState({
+ selectedTemplate: template,
+ })
+
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ onToggle = () => {
+ this.setState((prevState) => {
+ return { visible: !prevState.visible }
+ });
+ }
+
+ onTemplateSelect = (template) => {
+ this.setState({
+ selectedTemplate: template
+ })
+ }
+
+ onSearch = (search) => {
+ this.setState({ search })
+ this.getItems({ fresh: true, q: search })
+ }
+
+ onSubmit = () => {
+ const { onChangeCallback, input: { onChange } } = this.props
+
+ const { selectedTemplate } = this.state
+
+ onChange(selectedTemplate.id)
+
+ onChangeCallback && onChangeCallback(selectedTemplate)
+
+ this.onToggle()
+ }
+
+ BOTTOM_ACTION = () => {
+ const { language } = this.props
+
+ return (
+
+
+
+ )
+ }
+
+ render() {
+ const {
+ containerStyle,
+ templates,
+ label,
+ icon,
+ placeholder,
+ meta,
+ language,
+ } = this.props;
+
+ const {
+ visible,
+ selectedTemplate: { name, id } = {},
+ } = this.state
+
+ return (
+
+
+
+
+ this.onToggle(),
+ title: Lng.t("header.template", { locale: language }),
+ titleStyle: headerTitle({ marginLeft: -19, marginRight: -19 }),
+ placement: "center",
+ hasCircle: false,
+ noBorder: false,
+ transparent: false,
+ }}
+ bottomDivider
+ defaultLayout
+ bottomAction={this.BOTTOM_ACTION()}
+ >
+
+ {templates.map((val, index) => (
+ this.onTemplateSelect(val)}
+ key={index}
+ >
+
+
+ {id === val.id &&
+
+ }
+
+
+ ))}
+
+
+
+ );
+ }
+}
diff --git a/src/features/invoices/components/TemplateField/styles.js b/src/features/invoices/components/TemplateField/styles.js
new file mode 100644
index 00000000..2ebab8e5
--- /dev/null
+++ b/src/features/invoices/components/TemplateField/styles.js
@@ -0,0 +1,47 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { widthPercentageToDP as wp } from 'react-native-responsive-screen';
+
+export default StyleSheet.create({
+ container: {
+ flex: 1,
+ marginTop: 10,
+ },
+ imageList: {
+ display: 'flex',
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ justifyContent: 'space-between',
+ marginHorizontal: -10,
+ },
+ image: {
+ height: 220,
+ width: 150,
+ borderWidth: 2,
+ borderColor: colors.lightGray,
+ marginVertical: 10,
+ width: wp(40),
+ resizeMode: 'stretch'
+ },
+ active: {
+ borderColor: colors.primary
+ },
+ imageContainer: {
+ position: 'relative',
+ overflow: 'visible',
+ marginHorizontal: 10,
+ },
+ iconStyle: {
+ padding: 3,
+ },
+ iconContainer: {
+ position: 'absolute',
+ top: 0,
+ right: -10,
+ zIndex: 100,
+ backgroundColor: colors.primary,
+ borderRadius: 20,
+ padding: 0,
+ margin: 0,
+ }
+});
diff --git a/src/features/invoices/constants.js b/src/features/invoices/constants.js
new file mode 100644
index 00000000..610efbd9
--- /dev/null
+++ b/src/features/invoices/constants.js
@@ -0,0 +1,211 @@
+import queryString from 'query-string';
+import Lng from "../../api/lang/i18n";
+import { colors } from "../../styles/colors";
+
+// Forms
+// -----------------------------------------
+export const INVOICE_SEARCH = 'invoiceForm/INVOICE_SEARCH';
+export const INVOICE_FORM = 'invoiceForm/INVOICE_EDIT';
+export const ITEM_FORM = 'item/ITEM_FORM';
+
+// Type
+// -----------------------------------------
+export const INVOICE_ADD = 'invoiceForm/INVOICE_ADD';
+export const INVOICE_EDIT = 'invoiceForm/INVOICE_EDIT';
+
+// Actions
+// -----------------------------------------
+export const INVOICES_TRIGGER_SPINNER = 'invoice/INVOICES_TRIGGER_SPINNER';
+export const GET_INVOICES = 'invoice/GET_INVOICES';
+export const SET_INVOICES = 'invoice/SET_INVOICES';
+
+export const CLEAR_INVOICES = 'invoice/CLEAR_INVOICES';
+export const CLEAR_INVOICE = 'invoice/CLEAR_INVOICE';
+export const GET_CREATE_INVOICE = 'invoice/GET_CREATE_INVOICE';
+export const GET_EDIT_INVOICE = 'invoice/GET_EDIT_INVOICE';
+export const SET_INVOICE = 'invoice/SET_INVOICE';
+export const SET_EDIT_INVOICE = 'invoice/SET_EDIT_INVOICE';
+export const CREATE_INVOICE = 'invoice/CREATE_INVOICE';
+export const EDIT_INVOICE = 'invoice/EDIT_INVOICE';
+export const REMOVE_INVOICE = 'invoice/REMOVE_INVOICE';
+export const REMOVE_FROM_INVOICES = 'invoice/REMOVE_FROM_INVOICES';
+export const CHANGE_INVOICE_STATUS = 'invoice/CHANGE_INVOICE_STATUS';
+export const SET_ACTIVE_TAB = 'invoice/SET_ACTIVE_TAB';
+
+// Items
+// -----------------------------------------
+export const SET_EDIT_INVOICE_ITEMS = 'invoice/SET_EDIT_INVOICE_ITEMS';
+export const REMOVE_INVOICE_ITEM = 'invoice/REMOVE_INVOICE_ITEM';
+export const REMOVE_INVOICE_ITEMS = 'invoice/REMOVE_INVOICE_ITEMS';
+export const ADD_ITEM = 'invoice/ADD_ITEM';
+export const EDIT_ITEM = 'invoice/EDIT_ITEM';
+export const GET_ITEMS = 'invoice/GET_ITEMS';
+export const SET_ITEMS = 'invoice/SET_ITEMS';
+export const SET_INVOICE_ITEMS = 'invoice/SET_INVOICE_ITEMS';
+export const REMOVE_ITEM = 'invoice/REMOVE_ITEM';
+export const ITEM_ADD = 'invoice/ITEM_ADD';
+export const ITEM_EDIT = 'invoice/ITEM_EDIT';
+
+export const ITEM_DISCOUNT_OPTION = [
+ {
+ key: 'none',
+ label: 'None',
+ },
+ {
+ key: 'fixed',
+ label: 'Fixed',
+ },
+ {
+ key: 'percentage',
+ label: 'Percentage',
+ },
+];
+
+export const INVOICE_DISCOUNT_OPTION = [
+ {
+ value: 'percentage',
+ label: 'Percentage',
+ displayLabel: '%',
+ },
+];
+
+
+export const INVOICES_TABS = {
+ DUE: 'DUE',
+ DRAFT: 'DRAFT',
+ ALL: 'ALL',
+};
+
+export const TAB_NAME = (name, language, Lng) => {
+ return Lng.t(`invoices.tabs.${name}`, { locale: language })
+};
+
+// Filter Invoice Mode
+// -----------------------------------------
+export const FILTER_INVOICE_STATUS = [
+ { label: 'DRAFT', value: 'DRAFT' },
+ { label: 'SENT', value: 'SENT' },
+ { label: 'VIEWED', value: 'VIEWED' },
+ { label: 'OVERDUE', value: 'DUE' },
+ { label: 'COMPLETED', value: 'COMPLETED' },
+]
+
+export const FILTER_INVOICE_PAID_STATUS = [
+ { label: 'UNPAID', value: 'UNPAID' },
+ { label: 'PAID', value: 'PAID' },
+ { label: 'PARTIALLY PAID', value: 'PARTIALLY_PAID' },
+]
+
+export const INVOICES_STATUS = {
+ OVERDUE: 'danger',
+ DRAFT: 'warning',
+ PAID: 'success',
+};
+
+export const INVOICES_STATUS_BG_COLOR = {
+ DRAFT: colors.warningLight,
+ SENT: colors.warningLight2,
+ VIEWED: colors.infoLight,
+ OVERDUE: colors.dangerLight,
+ COMPLETED: colors.successLight2,
+ UNPAID: colors.warningLight,
+ PAID: colors.successLight2,
+ PARTIALLY_PAID: colors.infoLight,
+};
+
+export const INVOICES_STATUS_TEXT_COLOR = {
+ DRAFT: colors.warningDark,
+ SENT: colors.warningDark2,
+ VIEWED: colors.infoDark,
+ OVERDUE: colors.dangerDark,
+ COMPLETED: colors.successDark,
+ UNPAID: colors.warningDark,
+ PAID: colors.successDark,
+ PARTIALLY_PAID: colors.infoDark,
+};
+
+// ActionSheet Actions
+// -----------------------------------------
+
+export const INVOICE_ACTIONS = {
+ VIEW: 'download',
+ SEND: 'send',
+ EDIT: 'edit',
+ DELETE: 'delete',
+ RECORD_PAYMENT: 'recordPayment',
+ MARK_AS_SENT: 'markAsSent',
+}
+export const ADD_INVOICE_ACTIONS = (language) => {
+
+ return [
+ {
+ label: Lng.t("invoices.actions.sendInvoice", { locale: language }),
+ value: INVOICE_ACTIONS.SEND
+ },
+ ]
+};
+
+export const EDIT_INVOICE_ACTIONS = (language, SentStatus = false, completeStatus = false) => {
+
+ const markActions = [
+ {
+ label: Lng.t("invoices.actions.sendInvoice", { locale: language }),
+ value: INVOICE_ACTIONS.SEND
+ },
+ {
+ label: Lng.t("invoices.actions.markAsSent", { locale: language }),
+ value: INVOICE_ACTIONS.MARK_AS_SENT
+ }
+ ]
+
+ const paymentAction = [{
+ label: Lng.t("invoices.actions.recordPayment", { locale: language }),
+ value: INVOICE_ACTIONS.RECORD_PAYMENT
+ }]
+
+ const deleteAction = [{
+ label: Lng.t("invoices.actions.delete", { locale: language }),
+ value: INVOICE_ACTIONS.DELETE
+ }]
+
+ if (SentStatus) {
+ return [
+ ...paymentAction,
+ ...deleteAction
+ ]
+ }
+ else if (completeStatus) {
+ return [...deleteAction]
+ }
+ else {
+ return [
+ ...markActions,
+ ...paymentAction,
+ ...deleteAction
+ ]
+ }
+
+};
+
+// Endpoint Api URL
+// -----------------------------------------
+
+export const GET_INVOICES_URL = (type, param) => `invoices?status=${type}&${queryString.stringify({
+ ...param,
+ orderByField: 'created_at',
+ orderBy: 'desc'
+})}`
+
+export const GET_ITEMS_URL = (q, search, page, limit) => `items?search=${q ? q : search}&page=${page}&limit=${limit}`
+
+export const CREATE_INVOICE_URL = () => `invoices`
+export const EDIT_INVOICE_URL = (invoice) => `invoices/${invoice.id}`
+export const REMOVE_INVOICE_URL = (id) => `invoices/${id}`
+
+export const CREATE_ITEM_URL = () => `items`
+export const EDIT_ITEM_URL = (item_id) => `items/${item_id}`
+
+export const GET_EDIT_INVOICE_URL = (id) => `invoices/${id}/edit`
+export const GET_CREATE_INVOICE_URL = () => `invoices/create`
+
+export const CHANGE_INVOICE_STATUS_URL = (action) => `invoices/${action}`
\ No newline at end of file
diff --git a/src/features/invoices/containers/Invoice/index.js b/src/features/invoices/containers/Invoice/index.js
new file mode 100644
index 00000000..213c14c0
--- /dev/null
+++ b/src/features/invoices/containers/Invoice/index.js
@@ -0,0 +1,82 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Invoice } from '../../components/Invoice';
+import { reduxForm, getFormValues } from 'redux-form';
+import { validate } from './validation';
+import * as InvoicesAction from '../../actions';
+import { INVOICE_FORM, INVOICE_EDIT } from '../../constants';
+import moment from 'moment';
+import * as CustomersAction from '../../../customers/actions';
+
+const mapStateToProps = (state, { navigation }) => {
+ const {
+ global: { language, taxTypes },
+ invoices: { loading, invoiceItems, invoiceData, items },
+ customers: { customers, loading: { customersLoading } },
+ } = state;
+
+ const { invoice = null, nextInvoiceNumber, invoiceTemplates } = invoiceData;
+
+ let type = navigation.getParam('type')
+
+ let isLoading = loading.initInvoiceLoading || (type === INVOICE_EDIT && !invoice)
+ || !nextInvoiceNumber
+
+ return {
+ initLoading: isLoading,
+ customersLoading,
+ loading: loading.invoiceLoading,
+ invoiceItems,
+ invoiceData,
+ items,
+ type,
+ customers,
+ itemsLoading: loading.itemsLoading,
+ language,
+ formValues: getFormValues(INVOICE_FORM)(state) || {},
+ taxTypes,
+ initialValues: !isLoading ? {
+ due_date: moment().add(7, 'days'),
+ invoice_date: moment(),
+ invoice_number: nextInvoiceNumber,
+ discount_type: 'fixed',
+ discount: 0,
+ taxes: [],
+ invoice_template_id: invoiceTemplates[0] && invoiceTemplates[0].id,
+ ...invoice,
+ customer: invoice && invoice.user,
+ template: invoice && invoice.invoice_template,
+ } : null
+ };
+};
+
+const mapDispatchToProps = {
+ getCreateInvoice: InvoicesAction.getCreateInvoice,
+ createInvoice: InvoicesAction.createInvoice,
+ getItems: InvoicesAction.getItems,
+ getEditInvoice: InvoicesAction.getEditInvoice,
+ editInvoice: InvoicesAction.editInvoice,
+ removeInvoiceItems: InvoicesAction.removeInvoiceItems,
+ removeInvoice: InvoicesAction.removeInvoice,
+ clearInvoice: InvoicesAction.clearInvoice,
+ changeInvoiceStatus: InvoicesAction.changeInvoiceStatus,
+ getCustomers: CustomersAction.getCustomers,
+};
+
+// Redux Forms
+const addInvoiceReduxForm = reduxForm({
+ form: INVOICE_FORM,
+ validate,
+})(Invoice);
+
+// connect
+const InvoiceContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(addInvoiceReduxForm);
+
+InvoiceContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default InvoiceContainer;
diff --git a/src/features/invoices/containers/Invoice/validation.js b/src/features/invoices/containers/Invoice/validation.js
new file mode 100644
index 00000000..fe9f7718
--- /dev/null
+++ b/src/features/invoices/containers/Invoice/validation.js
@@ -0,0 +1,27 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const { invoice_date, invoice_number, due_date, user_id, items, invoice_template_id } = values;
+
+ errors.invoice_date = getError(invoice_date, ['required']);
+ errors.due_date = getError(due_date, ['required']);
+ errors.invoice_number = getError(invoice_number, ['requiredField']);
+
+ errors.items = getError(items, ['requiredCheckArray']);
+
+ errors.user_id = getError(
+ user_id,
+ ['requiredField'],
+ );
+
+ errors.invoice_template_id = getError(
+ invoice_template_id,
+ ['requiredField'],
+ );
+
+ return errors;
+};
diff --git a/src/features/invoices/containers/Invoices/index.js b/src/features/invoices/containers/Invoices/index.js
new file mode 100644
index 00000000..cf14415d
--- /dev/null
+++ b/src/features/invoices/containers/Invoices/index.js
@@ -0,0 +1,77 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Invoices } from '../../components/Invoices';
+import { reduxForm, getFormValues } from 'redux-form';
+import * as InvoicesAction from '../../actions';
+import { colors } from '../../../../styles/colors';
+import { INVOICE_SEARCH } from '../../constants';
+import { SvgXml } from 'react-native-svg';
+import { INVOICES } from '../../../../assets/svg';
+import { getCustomers } from '../../../customers/actions';
+import { getTitleByLanguage, tabBarOnPress, navigateTabRoutes, navigateRoute } from '../../../../navigation/actions';
+import { ROUTES } from '../../../../navigation/routes';
+
+const mapStateToProps = (state) => {
+
+ const {
+ global: { language },
+ customers: { customers },
+ invoices: {
+ invoices,
+ loading: { invoicesLoading }
+ }
+ } = state;
+
+ return {
+ invoices,
+ loading: invoicesLoading,
+ language,
+ customers,
+ formValues: getFormValues(INVOICE_SEARCH)(state) || {},
+
+ };
+};
+
+const mapDispatchToProps = {
+ getInvoices: InvoicesAction.getInvoices,
+ clearInvoices: InvoicesAction.clearInvoices,
+ setInvoiceActiveTab: InvoicesAction.setInvoiceActiveTab,
+ getCustomers: getCustomers
+};
+
+// Redux Forms
+const invoiceSearchReduxForm = reduxForm({
+ form: INVOICE_SEARCH,
+})(Invoices);
+
+// connect
+const InvoicesContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(invoiceSearchReduxForm);
+
+InvoicesContainer.navigationOptions = ({ navigation }) => ({
+ gesturesEnabled: false,
+ tabBarLabel: getTitleByLanguage('tabNavigation.invoices'),
+ tabBarIcon: ({ focused }: { focused: boolean }) => (
+
+ ),
+ tabBarOnPress: () => {
+
+ navigateTabRoutes(ROUTES.MAIN_INVOICES, { apiCall: false })
+
+ let apiCall = navigation.getParam('apiCall', false)
+
+ apiCall ? navigateRoute(ROUTES.MAIN_INVOICES) : tabBarOnPress(
+ ROUTES.MAIN_INVOICES,
+ InvoicesAction.getInvoices
+ )
+ }
+});
+
+export default InvoicesContainer;
diff --git a/src/features/invoices/containers/Item/index.js b/src/features/invoices/containers/Item/index.js
new file mode 100644
index 00000000..c81ad1cf
--- /dev/null
+++ b/src/features/invoices/containers/Item/index.js
@@ -0,0 +1,66 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { InvoiceItem } from '../../components/Item';
+import { reduxForm, getFormValues } from 'redux-form';
+import { validate } from './validation';
+import * as InvoicesAction from '../../actions';
+import { ITEM_FORM } from '../../constants';
+
+const mapStateToProps = (state, { navigation }) => {
+ const {
+ invoices: { loading },
+ global: { language, taxTypes },
+ } = state;
+
+ const item = navigation.getParam('item', {});
+
+ const type = navigation.getParam('type');
+ const discountPerItem = navigation.getParam('discount_per_item');
+ const taxPerItem = navigation.getParam('tax_per_item');
+
+ const isLoading = loading.editItemLoading || loading.removeItemLoading
+
+ return {
+ loading: isLoading,
+ formValues: getFormValues(ITEM_FORM)(state) || {},
+ itemId: item && (item.item_id || item.id),
+ taxTypes,
+ currency: navigation.getParam('currency'),
+ language: language,
+ discountPerItem,
+ taxPerItem,
+ type,
+ initialValues: {
+ price: null,
+ quantity: 1,
+ discount_type: 'none',
+ discount: 0,
+ taxes: [],
+ ...item
+ },
+ };
+};
+
+const mapDispatchToProps = {
+ addItem: InvoicesAction.addItem,
+ setInvoiceItems: InvoicesAction.setInvoiceItems,
+ removeInvoiceItem: InvoicesAction.removeInvoiceItem,
+};
+
+// Redux Forms
+const addItemReduxForm = reduxForm({
+ form: ITEM_FORM,
+ validate,
+})(InvoiceItem);
+
+// connect
+const InvoiceItemContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(addItemReduxForm);
+
+InvoiceItemContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default InvoiceItemContainer;
diff --git a/src/features/invoices/containers/Item/validation.js b/src/features/invoices/containers/Item/validation.js
new file mode 100644
index 00000000..dc70d576
--- /dev/null
+++ b/src/features/invoices/containers/Item/validation.js
@@ -0,0 +1,42 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const {
+ name,
+ description,
+ quantity,
+ discount_type,
+ price,
+ discount,
+ } = values;
+
+ errors.name = getError(name, ['requiredField']);
+
+ errors.quantity = getError(
+ quantity,
+ ['minNumberRequired', 'isNumberFormat'],
+ options = { fieldName: 'quantity', minNumber: 0 }
+ );
+
+ errors.price = getError(
+ price,
+ ['minNumberRequired', 'isNumberFormat'],
+ options = { fieldName: 'price', minNumber: 0 }
+ );
+
+ if (discount_type !== 'none') {
+ errors.discount = getError(
+ discount,
+ ['minNumberRequired', 'isNumberFormat'],
+ options = { fieldName: 'discount', minNumber: 0 }
+ );
+ }
+
+ errors.discount_type = getError(discount_type, ['required']);
+
+ return errors;
+};
diff --git a/src/features/invoices/reducers/index.js b/src/features/invoices/reducers/index.js
new file mode 100644
index 00000000..f60f884a
--- /dev/null
+++ b/src/features/invoices/reducers/index.js
@@ -0,0 +1,126 @@
+import {
+ SET_INVOICES,
+ CLEAR_INVOICES,
+ INVOICES_TRIGGER_SPINNER,
+ GET_INVOICES,
+ GET_ITEMS,
+ SET_ITEMS,
+ SET_CREATE_INVOICE,
+ SET_INVOICE_ITEMS,
+ SET_EDIT_INVOICE_ITEMS,
+ REMOVE_INVOICE_ITEM,
+ SET_EDIT_INVOICE,
+ REMOVE_INVOICE_ITEMS,
+ CLEAR_INVOICE,
+ SET_INVOICE,
+ REMOVE_FROM_INVOICES,
+ SET_ACTIVE_TAB,
+ INVOICES_TABS
+} from "../constants";
+
+const initialState = {
+ invoices: [],
+ filterInvoices: [],
+ items: [],
+ createInvoiceItem: {
+ invoiceTemplates: []
+ },
+ errors: null,
+ loading: {
+ invoicesLoading: false,
+ itemsLoading: false,
+ invoiceLoading: false,
+ initInvoiceLoading: false
+ },
+ invoiceData: {
+ invoice: null,
+ invoiceTemplates: [],
+ nextInvoiceNumber: ''
+ },
+ invoiceItems: [],
+ activeTab: 'UNPAID',
+
+};
+
+export default function invoicesReducer(state = initialState, action) {
+ const { payload, type } = action;
+
+ switch (type) {
+ case SET_INVOICES:
+ let { invoices, fresh, prepend } = payload;
+
+ if (prepend) {
+ return { ...state, invoices: [...invoices, ...state.invoices] };
+ }
+
+ if (!fresh) {
+ return { ...state, invoices: [...state.invoices, ...invoices] };
+ }
+
+ return { ...state, invoices };
+
+ case CLEAR_INVOICES:
+ return { ...state, invoices: [] };
+
+ case CLEAR_INVOICE:
+ return {
+ ...state,
+ invoiceItems: [],
+ items: [],
+ invoiceData: {
+ invoice: null,
+ invoiceTemplates: []
+ }
+ };
+
+ case SET_INVOICE:
+
+ return { ...state, invoiceData: payload };
+
+ case SET_EDIT_INVOICE:
+ return { ...state, ...payload };
+
+ case INVOICES_TRIGGER_SPINNER:
+ return { ...state, loading: { ...state.loading, ...payload } };
+
+ case SET_ITEMS:
+
+ const { items } = payload;
+
+ if (!payload.fresh) {
+ return { ...state, items: [...state.items, ...items] };
+ }
+ return { ...state, items };
+
+ case SET_INVOICE_ITEMS:
+
+ const { invoiceItem } = payload;
+
+ return { ...state, invoiceItems: [...state.invoiceItems, ...invoiceItem] };
+
+ case REMOVE_INVOICE_ITEM:
+
+ const { id } = payload;
+
+ const invoiceItems = state.invoiceItems.filter(val => (val.item_id || val.id) !== id)
+
+ return { ...state, invoiceItems };
+
+ case REMOVE_INVOICE_ITEMS:
+
+ return { ...state, invoiceItems: [] };
+
+ case REMOVE_FROM_INVOICES:
+
+ const newInvoices = state.invoices.filter(val => val.id !== payload.id)
+
+ return { ...state, invoices: newInvoices };
+
+ case SET_ACTIVE_TAB:
+
+ return { ...state, activeTab: payload.activeTab };
+
+ default:
+ return state;
+ }
+}
diff --git a/src/features/invoices/saga/index.js b/src/features/invoices/saga/index.js
new file mode 100644
index 00000000..ccc40aee
--- /dev/null
+++ b/src/features/invoices/saga/index.js
@@ -0,0 +1,426 @@
+import { call, put, takeEvery } from 'redux-saga/effects';
+import Request from '../../../api/request';
+import {
+ GET_INVOICES,
+ GET_CREATE_INVOICE,
+ GET_ITEMS,
+ ADD_ITEM,
+ CREATE_INVOICE,
+ EDIT_ITEM,
+ REMOVE_ITEM,
+ GET_EDIT_INVOICE,
+ EDIT_INVOICE,
+ REMOVE_INVOICE,
+ CHANGE_INVOICE_STATUS,
+ // Endpoint Api URL
+ GET_INVOICES_URL,
+ GET_CREATE_INVOICE_URL,
+ GET_EDIT_INVOICE_URL,
+ CREATE_ITEM_URL,
+ EDIT_ITEM_URL,
+ CREATE_INVOICE_URL,
+ EDIT_INVOICE_URL,
+ GET_ITEMS_URL,
+ REMOVE_INVOICE_URL,
+ CHANGE_INVOICE_STATUS_URL,
+} from '../constants';
+import {
+ invoiceTriggerSpinner,
+ setInvoices,
+ setItems,
+ setInvoiceItems,
+ removeInvoiceItem,
+ removeInvoiceItems,
+ setInvoice,
+ removeFromInvoices,
+} from '../actions';
+import { store } from '../../../store';
+import { checkConnection } from '../../../api/helper';
+import { ROUTES } from '../../../navigation/routes';
+
+
+function* getInvoices(payloadData) {
+ const {
+ payload: {
+ onResult = null,
+ fresh = true,
+ type = '',
+ onMeta = null,
+ params = null,
+ pagination: { page = 1, limit = 10 } = {},
+ } = {},
+ } = payloadData;
+
+ yield put(invoiceTriggerSpinner({ invoicesLoading: true }));
+
+ try {
+ yield call(checkConnection, payloadData);
+ let param = {
+ ...params,
+ page,
+ limit
+ }
+
+ const options = {
+ path: GET_INVOICES_URL(type, param),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setInvoices({ invoices: response.invoices.data, fresh }));
+
+ onMeta && onMeta(response.invoices);
+
+ onResult && onResult(true);
+ } catch (error) {
+ onResult && onResult(false);
+ } finally {
+ yield put(invoiceTriggerSpinner({ invoicesLoading: false }));
+ }
+}
+
+function* getCreateInvoice(payloadData) {
+ const {
+ payload: { onResult },
+ } = payloadData;
+
+ yield put(invoiceTriggerSpinner({ initInvoiceLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_CREATE_INVOICE_URL(),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setInvoice(response));
+
+ onResult && onResult(response);
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(invoiceTriggerSpinner({ initInvoiceLoading: false }));
+ }
+}
+
+function* getEditInvoice(payloadData) {
+ const {
+ payload: { id, onResult },
+ } = payloadData;
+
+ yield put(invoiceTriggerSpinner({ initInvoiceLoading: true }));
+
+ try {
+
+ const { settings: { taxTypes } } = store.getState();
+
+ const options = {
+ path: GET_EDIT_INVOICE_URL(id),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setInvoice(response));
+
+ yield put(removeInvoiceItems());
+
+ yield put(setInvoiceItems({ invoiceItem: response.invoice.items }));
+
+ onResult && onResult(response.invoice);
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(invoiceTriggerSpinner({ initInvoiceLoading: false }));
+ }
+}
+
+function* addItem(payloadData) {
+ const {
+ payload: {
+ item,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(invoiceTriggerSpinner({ createInvoiceItemLoading: true }));
+
+ try {
+
+ const { price, name, description, taxes, unit } = item
+
+ const options = {
+ path: CREATE_ITEM_URL(),
+ body: {
+ name,
+ description,
+ price,
+ unit,
+ taxes
+ }
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ const invoiceItem = [{
+ ...response.item,
+ item_id: response.item.id,
+ ...item
+ }]
+
+ yield put(setInvoiceItems({ invoiceItem }));
+
+ onResult && onResult()
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(invoiceTriggerSpinner({ createInvoiceItemLoading: false }));
+ }
+}
+
+function* editItem(payloadData) {
+ const {
+ payload: {
+ item,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(invoiceTriggerSpinner({ createInvoiceItemLoading: true }));
+
+ try {
+
+ const { price, name, description, item_id } = item
+
+ const options = {
+ path: EDIT_ITEM_URL(item_id),
+ body: {
+ name,
+ description,
+ price,
+ }
+ };
+
+ const response = yield call([Request, 'put'], options);
+
+ const invoiceItem = [{
+ ...response.item,
+ ...item,
+ }]
+
+ yield put(removeInvoiceItem({ id: invoiceItem.id }));
+
+ yield put(setInvoiceItems({ invoiceItem }));
+
+ onResult && onResult()
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(invoiceTriggerSpinner({ createInvoiceItemLoading: false }));
+ }
+}
+
+function* createInvoice(payloadData) {
+ const {
+ payload: {
+ invoice,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(invoiceTriggerSpinner({ invoiceLoading: true }));
+
+ try {
+
+ const options = {
+ path: CREATE_INVOICE_URL(),
+ body: invoice,
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ if (!(response.error)) {
+ yield put(removeInvoiceItems())
+
+ yield put(setInvoices({ invoices: [response.invoice], prepend: true }));
+
+ onResult && onResult(response.url)
+ }
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(invoiceTriggerSpinner({ invoiceLoading: false }));
+ }
+}
+
+function* editInvoice(payloadData) {
+ const {
+ payload: {
+ invoice,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(invoiceTriggerSpinner({ invoiceLoading: true }));
+
+ try {
+
+ const options = {
+ path: EDIT_INVOICE_URL(invoice),
+ body: invoice
+ };
+
+ const response = yield call([Request, 'put'], options);
+
+ onResult && onResult(response.url)
+
+ yield put(removeFromInvoices({ id: invoice.id }))
+
+ yield put(setInvoices({ invoices: [response.invoice], prepend: true }));
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(invoiceTriggerSpinner({ invoiceLoading: false }));
+ }
+}
+
+function* getItems(payloadData) {
+ const {
+ payload: {
+ onResult,
+ fresh,
+ onMeta,
+ search = '',
+ q = '',
+ pagination: { page, limit },
+ },
+ } = payloadData;
+
+ yield put(invoiceTriggerSpinner({ itemsLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_ITEMS_URL(q, search, page, limit),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setItems({ items: response.items.data, fresh }));
+
+ onMeta && onMeta(response.items);
+
+ onResult && onResult(response.items);
+ } catch (error) {
+ onResult && onResult(response.items);
+ } finally {
+ yield put(invoiceTriggerSpinner({ itemsLoading: false }));
+ }
+}
+
+function* removeItem(payloadData) {
+ const {
+ payload: {
+ onResult,
+ id,
+ },
+ } = payloadData;
+
+ yield put(invoiceTriggerSpinner({ removeItemLoading: true }));
+
+ try {
+
+ yield put(removeInvoiceItem({ id }));
+
+ onResult && onResult();
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(invoiceTriggerSpinner({ removeItemLoading: false }));
+ }
+}
+
+function* removeInvoice(payloadData) {
+ const {
+ payload: {
+ onResult,
+ id,
+ },
+ } = payloadData;
+
+ yield put(invoiceTriggerSpinner({ invoiceLoading: true }));
+
+ try {
+
+
+ const options = {
+ path: REMOVE_INVOICE_URL(id),
+ };
+
+ const response = yield call([Request, 'delete'], options);
+
+ if (response.success)
+ yield put(removeFromInvoices({ id }))
+
+ onResult && onResult(response);
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(invoiceTriggerSpinner({ invoiceLoading: false }));
+ }
+}
+
+function* changeInvoiceStatus(payloadData) {
+ const {
+ payload: {
+ onResult,
+ id,
+ action,
+ navigation
+ },
+ } = payloadData;
+
+ yield put(invoiceTriggerSpinner({ invoiceLoading: true }));
+
+ try {
+
+ const options = {
+ path: CHANGE_INVOICE_STATUS_URL(action),
+ body: { id }
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ if (response.success) {
+ navigation.navigate(ROUTES.MAIN_INVOICES)
+ yield call(getInvoices, payload = {});
+ }
+
+ onResult && onResult();
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(invoiceTriggerSpinner({ invoiceLoading: false }));
+ }
+}
+
+export default function* invoicesSaga() {
+ yield takeEvery(GET_INVOICES, getInvoices);
+ yield takeEvery(GET_CREATE_INVOICE, getCreateInvoice);
+ yield takeEvery(GET_EDIT_INVOICE, getEditInvoice);
+ yield takeEvery(ADD_ITEM, addItem);
+ yield takeEvery(GET_ITEMS, getItems);
+ yield takeEvery(CREATE_INVOICE, createInvoice);
+ yield takeEvery(EDIT_INVOICE, editInvoice);
+ yield takeEvery(EDIT_ITEM, editItem);
+ yield takeEvery(REMOVE_ITEM, removeItem);
+ yield takeEvery(REMOVE_INVOICE, removeInvoice);
+ yield takeEvery(CHANGE_INVOICE_STATUS, changeInvoiceStatus);
+}
diff --git a/src/features/more/actions/index.js b/src/features/more/actions/index.js
new file mode 100644
index 00000000..a367bb77
--- /dev/null
+++ b/src/features/more/actions/index.js
@@ -0,0 +1,82 @@
+import {
+ MORE_TRIGGER_SPINNER,
+ LOGOUT,
+ GET_ITEMS,
+ SET_ITEMS,
+ ITEM_ADD,
+ CLEAR_ITEM,
+ GET_EDIT_ITEM,
+ REMOVE_ITEM,
+ ITEM_EDIT,
+ SET_ITEM,
+ DELETE_ITEM,
+ GENERATE_REPORT,
+ SET_FILTER_ITEMS
+} from "../constants";
+
+export const moreTriggerSpinner = (payload) => ({
+ type: MORE_TRIGGER_SPINNER,
+ payload,
+});
+
+export const logout = (payload) => ({
+ type: LOGOUT,
+ payload,
+});
+
+export const addItem = (payload = {}) => ({
+ type: ITEM_ADD,
+ payload,
+});
+
+export const editItem = (payload = {}) => ({
+ type: ITEM_EDIT,
+ payload,
+});
+
+export const getItems = (payload = {}) => ({
+ type: GET_ITEMS,
+ payload,
+});
+
+export const removeItem = (payload = {}) => ({
+ type: REMOVE_ITEM,
+ payload,
+});
+
+export const setItems = (payload = {}) => ({
+ type: SET_ITEMS,
+ payload,
+});
+
+export const setFilterItems = (payload = {}) => ({
+ type: SET_FILTER_ITEMS,
+ payload,
+});
+
+export const setItem = (payload = {}) => ({
+ type: SET_ITEM,
+ payload,
+});
+
+export const clearItem = (payload = {}) => ({
+ type: CLEAR_ITEM,
+ payload,
+});
+
+export const deleteItem = (payload = {}) => ({
+ type: DELETE_ITEM,
+ payload,
+});
+
+export const getEditItem = (payload = {}) => ({
+ type: GET_EDIT_ITEM,
+ payload,
+});
+
+
+// Reports
+export const generateReport = (payload) => ({
+ type: GENERATE_REPORT,
+ payload,
+});
diff --git a/src/features/more/components/Item/index.js b/src/features/more/components/Item/index.js
new file mode 100644
index 00000000..19c47083
--- /dev/null
+++ b/src/features/more/components/Item/index.js
@@ -0,0 +1,458 @@
+// @flow
+
+import React from 'react';
+import {
+ View,
+ Text,
+ Alert,
+} from 'react-native';
+import styles from './styles';
+import { Field, change } from 'redux-form';
+import {
+ InputField,
+ CtDivider,
+ CtButton,
+ DefaultLayout,
+ SelectField,
+ SelectPickerField,
+ CurrencyFormat,
+} from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import { ITEM_FORM, EDIT_ITEM, ADD_ITEM, ITEM_UNITS } from '../../constants';
+import { BUTTON_COLOR } from '../../../../api/consts/core';
+import { colors } from '../../../../styles/colors';
+import Lng from '../../../../api/lang/i18n';
+import { goBack, UNMOUNT, MOUNT } from '../../../../navigation/actions';
+import { ADD_TAX } from '../../../settings/constants';
+import { MAX_LENGTH, alertMe } from '../../../../api/global';
+
+export class Item extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ }
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation)
+ }
+ componentWillMount() {
+ const { getEditItem, type, itemId } = this.props;
+
+ const isEdit = (type === EDIT_ITEM)
+
+ isEdit && getEditItem({ id: itemId })
+ }
+
+ componentWillUnmount() {
+ const { clearItem } = this.props
+ clearItem()
+ goBack(UNMOUNT)
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(ITEM_FORM, field, value));
+ };
+
+ saveItem = (values) => {
+ const {
+ addItem,
+ editItem,
+ itemId,
+ navigation,
+ type,
+ language,
+ } = this.props
+
+ if (this.finalAmount() < 0) {
+ alert(Lng.t("items.lessAmount", { locale: language }))
+ return
+ }
+
+ const item = {
+ ...values,
+ total: this.finalAmount(),
+ tax: this.itemTax() + this.itemCompoundTax(),
+ taxes: values.taxes && values.taxes.map(val => {
+ return {
+ ...val,
+ amount: val.compound_tax ?
+ this.getCompoundTaxValue(val.percent) :
+ this.getTaxValue(val.percent),
+ }
+ }),
+ }
+
+ type == ADD_ITEM ? addItem({
+ item,
+ onResult: () => {
+ navigation.navigate(ROUTES.GLOBAL_ITEMS)
+ }
+ }) : editItem({
+ item: { ...item },
+ id: itemId,
+ onResult: () => {
+ navigation.navigate(ROUTES.GLOBAL_ITEMS)
+ }
+ })
+
+ };
+
+ removeItem = () => {
+ const { removeItem, itemId, navigation, language } = this.props
+ Alert.alert(
+ Lng.t("alert.title", { locale: language }),
+ Lng.t("items.alertDescription", { locale: language }),
+ [
+ {
+ text: 'OK',
+ onPress: () => {
+ removeItem({
+ id: itemId,
+ onResult: (res) => {
+ if (res.error) {
+ res.error === 'item_attached' && alertMe({
+ title: Lng.t("items.alreadyAttachTitle", { locale: language }),
+ desc: Lng.t("items.alreadyAttachDescription", { locale: language })
+ })
+ }
+ else {
+ navigation.navigate(ROUTES.GLOBAL_ITEMS)
+ }
+
+ }
+ })
+ }
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+
+ }
+
+ totalAmount = () => {
+ const { formValues: { price } } = this.props
+
+ return price + this.itemTax()
+ }
+
+ itemTax = () => {
+ const { formValues: { taxes } } = this.props
+
+ let totalTax = 0
+
+ taxes && taxes.map(val => {
+ if (!val.compound_tax) {
+ totalTax += this.getTaxValue(val.percent)
+ }
+ })
+
+ return totalTax
+ }
+
+ itemCompoundTax = () => {
+ const { formValues: { taxes } } = this.props
+
+ let totalTax = 0
+
+ taxes && taxes.map(val => {
+ if (val.compound_tax) {
+ totalTax += this.getCompoundTaxValue(val.percent)
+ }
+ })
+
+ return totalTax
+ }
+
+ getTaxValue = (tax) => {
+ const { formValues: { price } } = this.props
+ return (tax * price) / 100
+ }
+
+ getCompoundTaxValue = (tax) => {
+ return (tax * JSON.parse(this.totalAmount())) / 100
+ }
+
+ getTaxName = (tax) => {
+ const { taxTypes } = this.props
+ let taxName = ''
+
+ const type = taxTypes && taxTypes.filter(val => val.fullItem.id === tax.tax_type_id)
+
+ if (taxTypes && type.length > 0) {
+ taxName = type[0]['fullItem'].name
+ }
+ return taxName
+ }
+
+ finalAmount = () => {
+ return this.totalAmount() + this.itemCompoundTax()
+ }
+
+ FINAL_AMOUNT = () => {
+ const {
+ language,
+ formValues: { taxes, price },
+ navigation,
+ } = this.props
+
+ const currency = navigation.getParam('currency')
+
+ return (
+
+
+
+
+ {Lng.t("items.subTotal", { locale: language })}
+
+
+
+
+
+
+
+ {taxes &&
+ taxes.map(val => !val.compound_tax ? (
+
+
+
+ {this.getTaxName(val)} ({val.percent} %)
+
+
+
+
+
+
+ ) : null)
+ }
+
+ {taxes &&
+ taxes.map(val => val.compound_tax ? (
+
+
+
+ {this.getTaxName(val)} ({val.percent} %)
+
+
+
+
+
+
+ ) : null)
+ }
+
+
+
+
+
+
+ {Lng.t("items.finalAmount", { locale: language })}
+
+
+
+
+
+
+
+ )
+ };
+
+ BOTTOM_ACTION = (handleSubmit) => {
+ const { language, loading, type } = this.props
+ const isCreateItem = (type === ADD_ITEM)
+
+ return (
+
+
+ {!isCreateItem && (
+
+ )}
+
+ )
+ }
+
+ render() {
+ const {
+ navigation,
+ handleSubmit,
+ loading,
+ language,
+ type,
+ taxTypes,
+ formValues: { taxes }
+ } = this.props;
+
+ const isCreateItem = (type === ADD_ITEM)
+ let itemRefs = {}
+
+ return (
+ navigation.navigate(ROUTES.GLOBAL_ITEMS),
+ title: isCreateItem ?
+ Lng.t("header.addItem", { locale: language }) :
+ Lng.t("header.editItem", { locale: language }),
+ placement: "center",
+ rightIcon: 'save',
+ rightIconProps: {
+ solid: true
+ },
+ rightIconPress: handleSubmit(this.saveItem),
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ loadingProps={{
+ is: loading,
+ }}
+ >
+
+ {
+ itemRefs.price.focus();
+ }
+ }}
+ />
+
+
+ {
+ itemRefs.price = ref;
+ }}
+ />
+
+
+
+ navigation.navigate(ROUTES.TAX, {
+ type: ADD_TAX,
+ onSelect: (val) => {
+ this.setFormField('taxes',
+ [...val, ...taxes]
+ )
+ }
+ })
+ }
+ emptyContentProps={{
+ contentType: "taxes",
+ }}
+ />
+
+ {this.FINAL_AMOUNT()}
+
+ {
+ itemRefs.description = ref;
+ }}
+ />
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/more/components/Item/styles.js b/src/features/more/components/Item/styles.js
new file mode 100644
index 00000000..04ea5137
--- /dev/null
+++ b/src/features/more/components/Item/styles.js
@@ -0,0 +1,135 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerTitle: {
+ fontSize: 17,
+ color: colors.dark1,
+ },
+ headerContainer: {
+ // backgroundColor: colors.veryLightGray,
+ },
+ taxFakeInput: {
+ color: colors.primary,
+ textAlign: 'right',
+ fontFamily: fonts.poppinsMedium,
+ fontSize: 16,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ dateFieldContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ marginHorizontal: -10
+ },
+ dateField: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingHorizontal: 10
+ },
+ inputFieldStyle: {
+ borderRadius: 0,
+ },
+ inputFieldContainer: {
+ paddingHorizontal: 0,
+ },
+ inputTextStyle: {
+ color: colors.dark3,
+ fontFamily: fonts.poppinsMedium,
+ },
+ inputFieldValidation: {
+ marginHorizontal: 0,
+ },
+ hintStyle: {
+ marginTop: -6,
+ paddingBottom: 6,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+ noteHintStyle: {
+ paddingBottom: 6,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+ fakeInputStyle: {
+ marginTop: 5,
+ },
+ amountContainer: {
+ borderWidth: 0.8,
+ borderColor: colors.lightGray,
+ marginTop: 24,
+ marginBottom: 20,
+ padding: 20,
+ backgroundColor: colors.white,
+ },
+ subContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ label: {
+ color: colors.gray,
+ fontFamily: fonts.poppinsMedium,
+ marginTop: 6,
+ },
+ price: {
+ color: colors.dark2,
+ fontSize: 16,
+ },
+ totalPrice: {
+ color: colors.primary,
+ fontSize: 18,
+ fontFamily: fonts.poppinsMedium,
+ },
+ divider: {
+ backgroundColor: colors.lightGray,
+ borderColor: colors.lightGray,
+ borderWidth: 0.7,
+ marginTop: 10,
+ marginBottom: 8,
+ },
+ submitButton: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ },
+ handleBtn: {
+ marginHorizontal: 9,
+ },
+ buttonContainer: {
+ flex: 1
+ },
+ itemContainer: {
+ marginVertical: 4,
+ borderWidth: 1,
+ borderColor: colors.lightGray
+ },
+ itemLeftTitle: {
+ fontSize: 15,
+ fontFamily: fonts.poppins,
+ color: colors.dark
+ },
+ itemLeftSubTitleLabel: {
+ marginLeft: -6,
+ },
+ itemLeftSubTitle: {
+ color: colors.darkGray,
+ fontSize: 13,
+ },
+ itemRightTitle: {
+ fontFamily: fonts.poppins,
+ fontSize: 18,
+ color: colors.secondary
+ },
+ selectPicker: {
+ marginTop: 17,
+ },
+});
diff --git a/src/features/more/components/Items/index.js b/src/features/more/components/Items/index.js
new file mode 100644
index 00000000..60be9a35
--- /dev/null
+++ b/src/features/more/components/Items/index.js
@@ -0,0 +1,355 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import { change } from 'redux-form';
+import styles from './styles';
+import {
+ MainLayout,
+ ListView
+} from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import { IMAGES } from '../../../../config';
+import Lng from '../../../../api/lang/i18n';
+import { ADD_ITEM, EDIT_ITEM, ITEM_SEARCH, ITEM_UNITS } from '../../constants';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { itemsDescriptionStyle } from '../../../invoices/components/Invoice/styles';
+
+type IProps = {
+ navigation: Object,
+ getItems: Function,
+ items: Object,
+ loading: Boolean,
+ language: String,
+}
+
+
+let params = {
+ search: '',
+ unit: '',
+ price: '',
+}
+
+export class Items extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ refreshing: false,
+ fresh: true,
+ pagination: {
+ page: 1,
+ limit: 10,
+ lastPage: 1,
+ },
+ search: '',
+ selectedUnit: '',
+ filter: false
+ };
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ this.getItems({ fresh: true });
+ goBack(MOUNT, navigation, ROUTES.MAIN_MORE)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ onItemSelect = ({ id }) => {
+ const { navigation } = this.props
+ navigation.navigate(ROUTES.GLOBAL_ITEM, { type: EDIT_ITEM, id })
+ this.onResetFilter()
+ }
+
+ getItems = ({
+ fresh = false,
+ onResult,
+ params,
+ filter = false
+ } = {}) => {
+ const { getItems } = this.props;
+ const { refreshing, pagination } = this.state;
+
+ if (refreshing) {
+ return;
+ }
+
+ this.setState({
+ refreshing: true,
+ fresh,
+ });
+
+ const paginationParams = fresh ? { ...pagination, page: 1 } : pagination;
+
+ if (!fresh && paginationParams.lastPage < paginationParams.page) {
+ return;
+ }
+
+ getItems({
+ fresh,
+ pagination: paginationParams,
+ params,
+ filter,
+ onMeta: ({ last_page, current_page }) => {
+ this.setState({
+ pagination: {
+ ...paginationParams,
+ lastPage: last_page,
+ page: current_page + 1,
+ },
+ });
+ },
+ onResult: (val) => {
+ this.setState({
+ refreshing: false,
+ fresh: !val,
+ });
+ onResult && onResult();
+ },
+ });
+ };
+
+
+ onResetFilter = () => {
+ this.setState({ filter: false })
+ }
+
+ onSubmitFilter = ({ unit = '', name = '', price = '' }) => {
+
+ if (unit || name || price) {
+ this.setState({ filter: true })
+
+ this.getItems({
+ fresh: true,
+ params: {
+ ...params,
+ search: name,
+ unit,
+ price,
+ },
+ filter: true
+ })
+ }
+ else
+ this.onResetFilter()
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(ITEM_SEARCH, field, value));
+
+ if (field === 'unit')
+ this.setState({ selectedUnit: value })
+ };
+
+ onSearch = (search) => {
+ this.onResetFilter()
+ this.setState({ search })
+ this.getItems({ fresh: true, params: { ...params, search } })
+ };
+
+ getItemList = (items) => {
+ const { currency } = this.props
+ let itemList = []
+
+ if (typeof items !== 'undefined' && items.length != 0) {
+
+ itemList = items.map((item) => {
+
+ let { name, description, price, title } = item
+
+ return {
+ title: title || name,
+ subtitle: {
+ title: description,
+ },
+ amount: price,
+ currency,
+ fullItem: item,
+ };
+ });
+ }
+
+ return itemList
+ }
+
+ loadMoreItems = () => {
+ const { search, filter } = this.state
+
+ const {
+ formValues: {
+ unit = '',
+ name = '',
+ price = ''
+ }
+ } = this.props
+
+ if (filter) {
+ this.getItems({
+ params: {
+ ...params,
+ search: name,
+ unit,
+ price,
+ },
+ filter: true
+ })
+ }
+ else
+ this.getItems({ params: { ...params, search } });
+ }
+
+
+ render() {
+
+ const {
+ navigation,
+ items,
+ filterItems,
+ loading,
+ language,
+ handleSubmit
+ } = this.props;
+
+ const {
+ refreshing,
+ pagination: { lastPage, page },
+ fresh,
+ search,
+ selectedUnit,
+ filter
+ } = this.state;
+
+ const canLoadMore = lastPage >= page;
+
+ let filterRefs = {}
+
+ let inputFields = [
+ {
+ name: 'name',
+ hint: Lng.t("items.name", { locale: language }),
+ inputProps: {
+ returnKeyType: 'next',
+ autoCorrect: true,
+ autoFocus: true,
+ onSubmitEditing: () => {
+ filterRefs.price.focus();
+ }
+ },
+ refLinkFn: (ref) => {
+ filterRefs.name = ref;
+ }
+ },
+ {
+ name: 'price',
+ hint: Lng.t("items.price", { locale: language }),
+ inputProps: {
+ returnKeyType: 'next',
+ keyboardType: 'numeric'
+ },
+ isCurrencyInput: true,
+ refLinkFn: (ref) => {
+ filterRefs.price = ref;
+ }
+ }
+ ]
+
+ let dropdownFields = [{
+ name: "unit",
+ label: Lng.t("items.unit", { locale: language }),
+ fieldIcon: 'align-center',
+ items: ITEM_UNITS,
+ onChangeCallback: (val) => {
+ this.setFormField('unit', val)
+ },
+ defaultPickerOptions: {
+ label: Lng.t("items.unitPlaceholder", { locale: language }),
+ value: '',
+
+ },
+ selectedItem: selectedUnit,
+ onDonePress: () => filterRefs.name.focus(),
+ containerStyle: styles.selectPicker
+ }]
+
+ let empty = (!filter && !search) ? {
+ description: Lng.t("items.empty.description", { locale: language }),
+ buttonTitle: Lng.t("items.empty.buttonTitle", { locale: language }),
+ buttonPress: () => {
+ navigation.navigate(ROUTES.GLOBAL_ITEM, { type: ADD_ITEM })
+ this.onResetFilter()
+ }
+ } : {}
+
+ let emptyTitle = search ? Lng.t("search.noResult", { locale: language, search })
+ : (!filter) ? Lng.t("items.empty.title", { locale: language }) :
+ Lng.t("filter.empty.filterTitle", { locale: language })
+
+
+ return (
+
+ navigation.navigate(ROUTES.MAIN_MORE),
+ title: Lng.t("header.items", { locale: language }),
+ titleStyle: styles.headerTitle,
+ rightIcon: "plus",
+ placement: "center",
+ rightIcon: "plus",
+ rightIconPress: () => {
+ navigation.navigate(ROUTES.GLOBAL_ITEM, { type: ADD_ITEM })
+ this.onResetFilter()
+ },
+ }}
+ onSearch={this.onSearch}
+ bottomDivider
+ onFocus={() => { }}
+ filter
+ filterProps={{
+ onSubmitFilter: handleSubmit(this.onSubmitFilter),
+ inputFields: inputFields,
+ dropdownFields: dropdownFields,
+ clearFilter: this.props,
+ }}
+ loadingProps={{ is: loading }}
+ >
+
+ {
+ this.onResetFilter()
+ this.getItems({
+ fresh: true,
+ onResult: onHide,
+ params: { ...params, search }
+ });
+ }}
+ getItems={() => {
+ this.loadMoreItems()
+ }}
+ bottomDivider
+ leftSubTitleStyle={itemsDescriptionStyle()}
+ emptyContentProps={{
+ title: emptyTitle,
+ image: IMAGES.EMPTY_ITEMS,
+ ...empty
+ }}
+ />
+
+
+
+
+ );
+ }
+}
+
diff --git a/src/features/more/components/Items/styles.js b/src/features/more/components/Items/styles.js
new file mode 100644
index 00000000..8ecc39aa
--- /dev/null
+++ b/src/features/more/components/Items/styles.js
@@ -0,0 +1,21 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { isIPhoneX } from '../../../../api/helper';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ listViewContainer: {
+ flex: 1,
+ paddingBottom: isIPhoneX() ? 30 : 0,
+ },
+ selectPicker: {
+ marginTop: 12,
+ marginBottom: 2,
+ },
+});
diff --git a/src/features/more/components/More/index.js b/src/features/more/components/More/index.js
new file mode 100644
index 00000000..8a8120b1
--- /dev/null
+++ b/src/features/more/components/More/index.js
@@ -0,0 +1,107 @@
+// @flow
+
+import React from 'react';
+import { View, Alert } from 'react-native';
+import styles from './styles';
+import { MainLayout, ListView } from '../../../../components';
+import { MORE_MENU } from '../../constants';
+import { colors } from '../../../../styles/colors';
+import Lng from '../../../../api/lang/i18n';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { ROUTES } from '../../../../navigation/routes';
+
+export class More extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ endpointVisible: false
+ }
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation, ROUTES.MAIN_INVOICES)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ onSelectMenu = (item) => {
+ const { navigation } = this.props
+
+ if (item.route) {
+ navigation.navigate(item.route)
+ } else {
+ this[item.action]()
+ }
+ }
+
+ onLogout = () => {
+ const { navigation, logout, language } = this.props
+
+ Alert.alert(
+ Lng.t("logout.confirmation", { locale: language }),
+ '',
+ [
+ {
+ text: Lng.t("logout.title", { locale: language }),
+ onPress: () => logout({ navigation })
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+
+ }
+
+ toggleEndpointModal = () => {
+ this.setState((state) => ({
+ endpointVisible: !state.endpointVisible
+ }))
+ }
+
+ render() {
+ const { language } = this.props;
+
+ return (
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/more/components/More/styles.js b/src/features/more/components/More/styles.js
new file mode 100644
index 00000000..cfb54380
--- /dev/null
+++ b/src/features/more/components/More/styles.js
@@ -0,0 +1,34 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ listViewContainer: {
+ flex: 1,
+ },
+ listViewScrollContainerStyle: {
+ paddingTop: 10
+ },
+ listViewTitle: {
+ fontFamily: fonts.poppins,
+ color: colors.secondary
+ },
+ listViewIcon: {
+ width: 28,
+ height: 29,
+ textAlign: "center",
+ },
+ itemContainer: {
+ marginVertical: -4,
+ },
+ dividerStyle: {
+ marginTop: 10
+ }
+});
diff --git a/src/features/more/components/Report/index.js b/src/features/more/components/Report/index.js
new file mode 100644
index 00000000..67d56ba3
--- /dev/null
+++ b/src/features/more/components/Report/index.js
@@ -0,0 +1,347 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import { Field, change } from 'redux-form';
+import styles from './styles';
+import {
+ CtButton,
+ DefaultLayout,
+ DatePickerField,
+ SelectPickerField,
+} from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import {
+ REPORT_FORM,
+ SALES,
+ PROFIT_AND_LOSS,
+ EXPENSES, TAXES,
+ DATE_RANGE_OPTION,
+ REPORT_TYPE_OPTION,
+ DATE_RANGE
+} from '../../constants';
+import { DATE_FORMAT, REPORT_DATE_FORMAT } from '../../../../api/consts/core';
+import Lng from '../../../../api/lang/i18n';
+import moment from 'moment';
+import { Linking } from 'expo';
+import { env } from '../../../../config';
+import QueryString from 'qs';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { headerTitle } from '../../../../api/helper';
+
+
+export class Report extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ taxTypeList: [],
+ displayFromDate: '',
+ displayToDate: '',
+ }
+ }
+
+ componentWillMount() {
+ const { taxTypes } = this.props;
+ this.setState({ taxTypeList: taxTypes })
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props;
+ goBack(MOUNT, navigation)
+ }
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(REPORT_FORM, field, value));
+ };
+
+ saveReport = ({ to_date, from_date, report_type }) => {
+ const {
+ company,
+ endpointURL
+ } = this.props
+
+ const params = { from_date, to_date }
+
+ const endPoint = endpointURL || env.ENDPOINT_URL
+
+ const report = this.getReport({ reportType: report_type })
+
+ Linking.openURL(`${endPoint}reports/${report}${company.unique_hash}?${QueryString.stringify(params)}`);
+
+ };
+
+ getThisDate = (type, time) => {
+ const date = moment()[type](time)
+
+ type === 'startOf' && this.setState({ displayFromDate: date })
+ type === 'endOf' && this.setState({ displayToDate: date })
+
+ return date.format(DATE_FORMAT)
+ }
+
+ getPreDate = (type, time) => {
+ const date = moment().subtract(1, time)[type](time)
+
+ type === 'startOf' && this.setState({ displayFromDate: date })
+ type === 'endOf' && this.setState({ displayToDate: date })
+
+ return date.format(DATE_FORMAT)
+ }
+
+ getCurrentFiscalDate = (type, time) => {
+ const { fiscalYear } = this.props
+ const firstMonth = JSON.parse(fiscalYear.split('-')[0]) - 1
+ const secondMonth = JSON.parse(fiscalYear.split('-')[1]) - 1
+
+ let date = moment()
+
+ if (type === 'startOf') {
+ date = date.month(firstMonth)[type]('month')
+ this.setState({ displayFromDate: date })
+ } else {
+ date = date.month(secondMonth).add(time, 1)[type]('month')
+ this.setState({ displayToDate: date })
+ }
+ return date.format(DATE_FORMAT)
+ }
+
+ getPreFiscalDate = (type, time) => {
+ const { fiscalYear } = this.props
+ const firstMonth = JSON.parse(fiscalYear.split('-')[0]) - 1
+ const secondMonth = JSON.parse(fiscalYear.split('-')[1]) - 1
+
+ let date = moment()
+
+ if (type === 'startOf') {
+ date = date.month(firstMonth).subtract(time, 1)[type]('month')
+
+ this.setState({ displayFromDate: date })
+ } else {
+ date = date.month(secondMonth)[type]('month')
+
+ this.setState({ displayToDate: date })
+ }
+
+ return date.format(DATE_FORMAT)
+ }
+
+ onDateRangeChange = (val) => {
+ this.setFormField('date_range', val)
+
+ switch (val) {
+ case DATE_RANGE.TODAY:
+ const displayDate = moment()
+ this.setFormField('from_date', displayDate.format(DATE_FORMAT))
+ this.setState({ displayFromDate: displayDate })
+ this.setFormField('to_date', displayDate.format(DATE_FORMAT))
+ this.setState({ displayToDate: displayDate })
+ break;
+
+ case DATE_RANGE.THIS_WEEK:
+ this.setFormField('from_date', this.getThisDate('startOf', 'isoWeek'))
+ this.setFormField('to_date', this.getThisDate('endOf', 'isoWeek'))
+ break;
+
+ case DATE_RANGE.THIS_MONTH:
+ this.setFormField('from_date', this.getThisDate('startOf', 'month'))
+ this.setFormField('to_date', this.getThisDate('endOf', 'month'))
+ break;
+
+ case DATE_RANGE.THIS_QUARTER:
+ this.setFormField('from_date', this.getThisDate('startOf', 'quarter'))
+ this.setFormField('to_date', this.getThisDate('endOf', 'quarter'))
+ break;
+
+ case DATE_RANGE.THIS_YEAR:
+ this.setFormField('from_date', this.getThisDate('startOf', 'year'))
+ this.setFormField('to_date', this.getThisDate('endOf', 'year'))
+ break;
+
+ case DATE_RANGE.CURRENT_FISCAL_QUARTER:
+ this.setFormField('from_date', this.getCurrentFiscalDate('startOf', 'quarter'))
+ this.setFormField('to_date', this.getCurrentFiscalDate('endOf', 'quarter'))
+ break;
+
+ case DATE_RANGE.CURRENT_FISCAL_YEAR:
+ this.setFormField('from_date', this.getCurrentFiscalDate('startOf', 'year'))
+ this.setFormField('to_date', this.getCurrentFiscalDate('endOf', 'year'))
+ break;
+
+ case DATE_RANGE.PREVIOUS_WEEK:
+ this.setFormField('from_date', this.getPreDate('startOf', 'isoWeek'))
+ this.setFormField('to_date', this.getPreDate('endOf', 'isoWeek'))
+ break;
+
+ case DATE_RANGE.PREVIOUS_MONTH:
+ this.setFormField('from_date', this.getPreDate('startOf', 'month'))
+ this.setFormField('to_date', this.getPreDate('endOf', 'month'))
+ break;
+
+ case DATE_RANGE.PREVIOUS_QUARTER:
+ this.setFormField('from_date', this.getPreDate('startOf', 'quarter'))
+ this.setFormField('to_date', this.getPreDate('endOf', 'quarter'))
+ break;
+
+ case DATE_RANGE.PREVIOUS_YEAR:
+ this.setFormField('from_date', this.getPreDate('startOf', 'year'))
+ this.setFormField('to_date', this.getPreDate('endOf', 'year'))
+ break;
+
+ case DATE_RANGE.PREVIOUS_FISCAL_QUARTER:
+ this.setFormField('from_date', this.getPreFiscalDate('startOf', 'quarter'))
+ this.setFormField('to_date', this.getPreFiscalDate('endOf', 'quarter'))
+ break;
+
+ case DATE_RANGE.PREVIOUS_FISCAL_YEAR:
+ this.setFormField('from_date', this.getPreFiscalDate('startOf', 'year'))
+ this.setFormField('to_date', this.getPreFiscalDate('endOf', 'year'))
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ BOTTOM_ACTION = (handleSubmit) => {
+ const { language, loading } = this.props
+
+ return (
+
+
+
+
+
+ )
+ }
+
+ getReport = ({ isTitle, reportType = '' }) => {
+ const { type, language } = this.props
+
+ let data = ''
+
+ switch (type) {
+ case SALES:
+ const tp = (reportType === 'byCustomer')
+
+ data = isTitle ?
+ Lng.t("header.salesReport", { locale: language }) :
+ (tp ? 'sales/customers/' : 'sales/items/')
+ break;
+
+ case PROFIT_AND_LOSS:
+ data = isTitle ?
+ Lng.t("header.profitAndLossReport", { locale: language }) : 'profit-loss/'
+ break;
+
+ case EXPENSES:
+ data = isTitle ?
+ Lng.t("header.expensesReport", { locale: language }) : 'expenses/'
+ break;
+
+ case TAXES:
+ data = isTitle ?
+ Lng.t("header.taxesReport", { locale: language }) : 'tax-summary/'
+ break;
+
+ default:
+ break;
+ }
+
+ return data
+ }
+
+ render() {
+ const {
+ navigation,
+ handleSubmit,
+ loading,
+ language,
+ initialValues,
+ type
+ } = this.props;
+
+ const { displayFromDate, displayToDate } = this.state
+
+ return (
+ navigation.navigate(ROUTES.REPORTS),
+ title: this.getReport({ isTitle: true }),
+ titleStyle: headerTitle({ marginLeft: -26, marginRight: -50 }),
+ placement: "center",
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ loadingProps={{
+ is: loading,
+ }}
+ >
+
+
+
+
+
+ {
+ this.setFormField('date_range', 'custom')
+ this.setState({ displayFromDate: '' })
+ }}
+ />
+
+
+ {
+ this.setFormField('date_range', 'custom')
+ this.setState({ displayToDate: '' })
+ }}
+ />
+
+
+
+ {type === SALES && (
+ {
+ this.setFormField('report_type', val)
+ }}
+ fakeInputContainerStyle={styles.selectPickerField}
+ />
+ )}
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/more/components/Report/styles.js b/src/features/more/components/Report/styles.js
new file mode 100644
index 00000000..1ad11502
--- /dev/null
+++ b/src/features/more/components/Report/styles.js
@@ -0,0 +1,132 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerTitle: {
+ fontSize: 17,
+ color: colors.dark1,
+ },
+ headerContainer: {
+ // backgroundColor: colors.veryLightGray,
+ },
+ taxFakeInput: {
+ color: colors.primary,
+ textAlign: 'right',
+ fontFamily: fonts.poppinsMedium,
+ fontSize: 16,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ dateFieldContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ marginHorizontal: -10
+ },
+ dateField: {
+ flex: 1,
+ justifyContent: 'space-between',
+ paddingHorizontal: 7
+ },
+ inputFieldStyle: {
+ borderRadius: 0,
+ },
+ inputFieldContainer: {
+ paddingHorizontal: 0,
+ },
+ inputTextStyle: {
+ color: colors.dark3,
+ fontFamily: fonts.poppinsMedium,
+ },
+ inputFieldValidation: {
+ marginHorizontal: 0,
+ },
+ hintStyle: {
+ marginTop: -6,
+ paddingBottom: 6,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+ noteHintStyle: {
+ paddingBottom: 6,
+ color: colors.dark2,
+ fontSize: 14,
+ fontFamily: fonts.poppins,
+ },
+ fakeInputStyle: {
+ marginTop: 5,
+ },
+
+ amountContainer: {
+ borderWidth: 0.8,
+ borderColor: colors.lightGray,
+ marginTop: 24,
+ padding: 20,
+ backgroundColor: colors.white,
+ },
+ subContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ label: {
+ color: colors.gray,
+ fontFamily: fonts.poppinsMedium,
+ marginTop: 6,
+ },
+ price: {
+ color: colors.dark2,
+ fontSize: 16,
+ },
+ totalPrice: {
+ color: colors.primary,
+ fontSize: 18,
+ fontFamily: fonts.poppinsMedium,
+ },
+ divider: {
+ backgroundColor: colors.lightGray,
+ borderColor: colors.lightGray,
+ borderWidth: 0.7,
+ marginTop: 10,
+ marginBottom: 8,
+ },
+ submitButton: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ },
+ handleBtn: {
+ marginHorizontal: 9,
+ },
+ btnWhite: {
+ // paddingVertical: 6,
+ },
+ itemContainer: {
+ marginVertical: 4,
+ borderWidth: 1,
+ borderColor: colors.lightGray
+ },
+ itemLeftTitle: {
+ fontSize: 15,
+ fontFamily: fonts.poppins,
+ color: colors.dark
+ },
+ itemLeftSubTitleLabel: {
+ marginLeft: -6,
+ },
+ itemLeftSubTitle: {
+ color: colors.darkGray,
+ fontSize: 13,
+ },
+ itemRightTitle: {
+ fontFamily: fonts.poppins,
+ fontSize: 18,
+ color: colors.secondary
+ },
+});
diff --git a/src/features/more/components/Reports/index.js b/src/features/more/components/Reports/index.js
new file mode 100644
index 00000000..b666e415
--- /dev/null
+++ b/src/features/more/components/Reports/index.js
@@ -0,0 +1,70 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import styles from './styles';
+import { ListView, DefaultLayout } from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import { colors } from '../../../../styles/colors';
+import Lng from '../../../../api/lang/i18n';
+import { REPORTS_MENU } from '../../constants';
+import { MOUNT, goBack, UNMOUNT } from '../../../../navigation/actions';
+
+export class Reports extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ endpointVisible: false
+ }
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation, ROUTES.MAIN_MORE)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ onSelectMenu = ({ route, type }) => {
+ const { navigation } = this.props
+
+ if (route) {
+ navigation.navigate(route, { type })
+ }
+ }
+
+ render() {
+ const { navigation, language } = this.props;
+
+ return (
+
+ navigation.navigate(ROUTES.MAIN_MORE),
+ title: Lng.t("header.reports", { locale: language }),
+ leftIconStyle: { color: colors.dark2 }
+ }}
+ hasSearchField={false}
+ >
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/more/components/Reports/styles.js b/src/features/more/components/Reports/styles.js
new file mode 100644
index 00000000..072559da
--- /dev/null
+++ b/src/features/more/components/Reports/styles.js
@@ -0,0 +1,23 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ listViewTitle: {
+ fontFamily: fonts.poppins,
+ color: colors.secondary
+ },
+ listViewContainer: {
+ marginTop: 20
+ },
+ listViewIcon: {
+ width: 25,
+ }
+});
diff --git a/src/features/more/constants.js b/src/features/more/constants.js
new file mode 100644
index 00000000..8d721fd5
--- /dev/null
+++ b/src/features/more/constants.js
@@ -0,0 +1,247 @@
+import queryString from 'query-string';
+import { ROUTES } from "../../navigation/routes";
+
+// Forms
+// -----------------------------------------
+export const SET_ENDPOINT_API = 'moreForm/SET_ENDPOINT_API';
+
+export const ITEM_FORM = 'moreForm/ITEM_FORM';
+export const ITEM_SEARCH = 'moreForm/ITEM_SEARCH';
+
+export const REPORTS_SEARCH = 'moreForm/REPORTS_SEARCH';
+export const REPORT_FORM = 'moreForm/REPORT_FORM';
+
+// Actions
+// -----------------------------------------
+export const MORE_SEARCH = 'more/MORE_SEARCH';
+export const MORE_TRIGGER_SPINNER = 'more/MORE_TRIGGER_SPINNER';
+export const LOGOUT = 'more/LOGOUT';
+export const ITEM_EDIT = 'more/ITEM_EDIT';
+export const ITEM_ADD = 'more/ITEM_ADD';
+export const CLEAR_ITEM = 'more/CLEAR_ITEM';
+export const DELETE_ITEM = 'more/DELETE_ITEM';
+export const REMOVE_ITEM = 'more/REMOVE_ITEM';
+export const GET_ITEMS = 'more/GET_ITEMS';
+export const GET_EDIT_ITEM = 'more/GET_EDIT_ITEM';
+export const SET_ITEMS = 'more/SET_ITEMS';
+export const SET_FILTER_ITEMS = 'more/SET_FILTER_ITEMS';
+export const SET_ITEM = 'more/SET_ITEM';
+
+// Report
+// -----------------------------------------
+export const GENERATE_REPORT = 'report/GENERATE_REPORT';
+
+
+export const ADD_ITEM = 'itemType/ADD_ITEM';
+export const EDIT_ITEM = 'itemType/EDIT_ITEM';
+export const SALES = 'reportType/SALES';
+export const PROFIT_AND_LOSS = 'reportType/PROFIT_AND_LOSS';
+export const EXPENSES = 'reportType/EXPENSES';
+export const TAXES = 'reportType/TAXES';
+
+
+
+// Menus
+// -----------------------------------------
+export const MORE_MENU = (language, Lng) => {
+ return [
+ {
+ title: Lng.t("more.estimate", { locale: language }),
+ leftIcon: 'file-alt',
+ iconSize: 28,
+ fullItem: {
+ route: ROUTES.ESTIMATE_LIST
+ }
+ },
+ {
+ title: Lng.t("more.items", { locale: language }),
+ leftIcon: 'product-hunt',
+ iconSize: 27,
+ fullItem: {
+ route: ROUTES.GLOBAL_ITEMS
+ }
+ },
+ {
+ title: Lng.t("more.reports", { locale: language }),
+ leftIcon: 'signal',
+ fullItem: {
+ route: ROUTES.REPORTS
+ }
+ },
+ {
+ title: Lng.t("more.settings", { locale: language }),
+ leftIcon: 'cogs',
+ fullItem: {
+ route: ROUTES.SETTING_LIST
+ }
+ },
+ {
+ title: Lng.t("more.logout", { locale: language }),
+ leftIcon: 'sign-out-alt',
+ iconSize: 26,
+ fullItem: {
+ action: 'onLogout'
+ }
+ },
+ ]
+}
+
+export const REPORTS_MENU = (language, Lng) => {
+ return [
+ {
+ title: Lng.t("reports.sales", { locale: language }),
+ fullItem: {
+ route: ROUTES.GENERATE_REPORT,
+ type: SALES
+ }
+ },
+ {
+ title: Lng.t("reports.profitAndLoss", { locale: language }),
+ leftIcon: 'building',
+ fullItem: {
+ route: ROUTES.GENERATE_REPORT,
+ type: PROFIT_AND_LOSS
+ }
+ },
+ {
+ title: Lng.t("reports.expenses", { locale: language }),
+ fullItem: {
+ route: ROUTES.GENERATE_REPORT,
+ type: EXPENSES
+ }
+ },
+ {
+ title: Lng.t("reports.taxes", { locale: language }),
+ fullItem: {
+ route: ROUTES.GENERATE_REPORT,
+ type: TAXES
+ }
+ },
+ ]
+}
+
+export const REPORT_TYPE_OPTION = (language, Lng) => {
+ return [
+ {
+ label: Lng.t("reports.byCustomer", { locale: language }),
+ value: 'byCustomer'
+ },
+ {
+ label: Lng.t("reports.byItem", { locale: language }),
+ value: 'byItem'
+ },
+ ]
+}
+
+export const DATE_RANGE = {
+ TODAY: 'today',
+ THIS_WEEK: 'thisWeek',
+ THIS_MONTH: 'thisMonth',
+ THIS_QUARTER: 'thisQuarter',
+ THIS_YEAR: 'thisYear',
+ CURRENT_FISCAL_QUARTER: 'currentFiscalQuarter',
+ CURRENT_FISCAL_YEAR: 'currentFiscalYear',
+ PREVIOUS_WEEK: 'previousWeek',
+ PREVIOUS_MONTH: 'previousMonth',
+ PREVIOUS_QUARTER: 'previousQuarter',
+ PREVIOUS_YEAR: 'previousYear',
+ PREVIOUS_FISCAL_QUARTER: 'previousFiscalQuarter',
+ PREVIOUS_FISCAL_YEAR: 'previousFiscalYear',
+ CUSTOM: 'custom',
+}
+
+export const DATE_RANGE_OPTION = (language, Lng) => {
+ return [
+ {
+ label: Lng.t("reports.today", { locale: language }),
+ value: DATE_RANGE.TODAY
+ },
+ {
+ label: Lng.t("reports.thisWeek", { locale: language }),
+ value: DATE_RANGE.THIS_WEEK
+ },
+ {
+ label: Lng.t("reports.thisMonth", { locale: language }),
+ value: DATE_RANGE.THIS_MONTH
+ },
+ {
+ label: Lng.t("reports.thisQuarter", { locale: language }),
+ value: DATE_RANGE.THIS_QUARTER
+ },
+ {
+ label: Lng.t("reports.thisYear", { locale: language }),
+ value: DATE_RANGE.THIS_YEAR
+ },
+ // {
+ // label: Lng.t("reports.currentFiscalQuarter", { locale: language }),
+ // value: DATE_RANGE.CURRENT_FISCAL_QUARTER
+ // },
+ {
+ label: Lng.t("reports.currentFiscalYear", { locale: language }),
+ value: DATE_RANGE.CURRENT_FISCAL_YEAR
+ },
+ {
+ label: Lng.t("reports.previousWeek", { locale: language }),
+ value: DATE_RANGE.PREVIOUS_WEEK
+ },
+ {
+ label: Lng.t("reports.previousMonth", { locale: language }),
+ value: DATE_RANGE.PREVIOUS_MONTH
+ },
+ {
+ label: Lng.t("reports.previousQuarter", { locale: language }),
+ value: DATE_RANGE.PREVIOUS_QUARTER
+ },
+ {
+ label: Lng.t("reports.previousYear", { locale: language }),
+ value: DATE_RANGE.PREVIOUS_YEAR
+ },
+ // {
+ // label: Lng.t("reports.previousFiscalQuarter", { locale: language }),
+ // value: DATE_RANGE.PREVIOUS_FISCAL_QUARTER
+ // },
+ {
+ label: Lng.t("reports.previousFiscalYear", { locale: language }),
+ value: DATE_RANGE.PREVIOUS_FISCAL_YEAR
+ },
+ {
+ label: Lng.t("reports.custom", { locale: language }),
+ value: DATE_RANGE.CUSTOM
+ },
+ ]
+}
+
+
+// Item Unit
+// -----------------------------------------
+export const ITEM_UNITS = [
+ { label: 'pc', value: 'pc' },
+ { label: 'box', value: 'box' },
+ { label: 'cm', value: 'cm' },
+ { label: 'dz', value: 'dz' },
+ { label: 'ft', value: 'ft' },
+ { label: 'g', value: 'g' },
+ { label: 'in', value: 'in' },
+ { label: 'kg', value: 'kg' },
+ { label: 'km', value: 'km' },
+ { label: 'lb', value: 'lb' },
+ { label: 'mg', value: 'mg' }
+]
+
+export const ITEM_DEFAULT_OPTION = {
+ label: 'box', value: 'box'
+}
+
+// Endpoint Api URL
+// -----------------------------------------
+
+export const GET_ITEMS_URL = (param) => `items?${queryString.stringify({
+ ...param,
+ orderByField: 'created_at',
+ orderBy: 'desc'
+})}`
+export const GET_EDIT_ITEMS_URL = (id) => `items/${id}/edit`
+
+export const CREATE_ITEM_URL = () => `items`
+export const EDIT_ITEM_URL = (id) => `items/${id}`
+export const REMOVE_ITEM_URL = (id) => `items/${id}`
diff --git a/src/features/more/containers/Item/index.js b/src/features/more/containers/Item/index.js
new file mode 100644
index 00000000..62c83929
--- /dev/null
+++ b/src/features/more/containers/Item/index.js
@@ -0,0 +1,63 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Item } from '../../components/Item';
+import { reduxForm, getFormValues } from 'redux-form';
+import { validate } from './validation';
+import * as MoreAction from '../../actions';
+import { ITEM_FORM, EDIT_ITEM } from '../../constants';
+
+const mapStateToProps = (state, { navigation }) => {
+ const {
+ more: { loading, item },
+ settings: { taxByItems },
+ global: { language, currency, taxTypes },
+ } = state;
+
+ const itemId = navigation.getParam('id', {});
+
+ const type = navigation.getParam('type');
+
+ const isLoading = loading.itemLoading || (type === EDIT_ITEM && !item)
+
+ return {
+ loading: isLoading,
+ formValues: getFormValues(ITEM_FORM)(state) || {},
+ itemId,
+ taxTypes,
+ taxByItems,
+ language,
+ type,
+ currency,
+ initialValues: !isLoading ? {
+ price: 0,
+ taxes: [],
+ ...item
+ } : null,
+ };
+};
+
+const mapDispatchToProps = {
+ addItem: MoreAction.addItem,
+ editItem: MoreAction.editItem,
+ getEditItem: MoreAction.getEditItem,
+ removeItem: MoreAction.removeItem,
+ clearItem: MoreAction.clearItem,
+};
+
+// Redux Forms
+const ItemReduxForm = reduxForm({
+ form: ITEM_FORM,
+ validate,
+})(Item);
+
+// connect
+const ItemContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(ItemReduxForm);
+
+ItemContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default ItemContainer;
diff --git a/src/features/more/containers/Item/validation.js b/src/features/more/containers/Item/validation.js
new file mode 100644
index 00000000..843d2072
--- /dev/null
+++ b/src/features/more/containers/Item/validation.js
@@ -0,0 +1,41 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const {
+ name,
+ quantity,
+ discount_type,
+ price,
+ discount,
+ } = values;
+
+ errors.name = getError(name, ['requiredField']);
+
+ errors.quantity = getError(
+ quantity,
+ ['minNumberRequired'],
+ options = { fieldName: 'quantity', minNumber: 0 }
+ );
+
+ errors.price = getError(
+ price,
+ ['minNumberRequired', 'isNumberFormat'],
+ options = { fieldName: 'price', minNumber: 0 }
+ );
+
+ if (discount_type !== 'none') {
+ errors.discount = getError(
+ discount,
+ ['minNumberRequired'],
+ options = { fieldName: 'discount', minNumber: 0 }
+ );
+ }
+
+ errors.discount_type = getError(discount_type, ['required']);
+
+ return errors;
+};
diff --git a/src/features/more/containers/Items/index.js b/src/features/more/containers/Items/index.js
new file mode 100644
index 00000000..7abe122d
--- /dev/null
+++ b/src/features/more/containers/Items/index.js
@@ -0,0 +1,44 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { reduxForm, getFormValues } from 'redux-form';
+import * as ItemsAction from '../../actions'
+import { Items } from '../../components/Items';
+import { ITEM_SEARCH } from '../../constants';
+
+
+const mapStateToProps = (state) => {
+
+ const {
+ more: { items, filterItems, loading },
+ global: { currency, language }
+ } = state;
+
+ return {
+ items,
+ filterItems,
+ loading: loading.itemsLoading,
+ language,
+ currency,
+ formValues: getFormValues(ITEM_SEARCH)(state) || {},
+ };
+};
+
+const mapDispatchToProps = {
+ getItems: ItemsAction.getItems,
+};
+// Redux Forms
+const ItemsSearchReduxForm = reduxForm({
+ form: ITEM_SEARCH
+})(Items);
+
+// connect
+const ItemsContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(ItemsSearchReduxForm);
+
+ItemsContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default ItemsContainer;
diff --git a/src/features/more/containers/More/index.js b/src/features/more/containers/More/index.js
new file mode 100644
index 00000000..47a9fd08
--- /dev/null
+++ b/src/features/more/containers/More/index.js
@@ -0,0 +1,45 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { More } from '../../components/More';
+import { colors } from '../../../../styles/colors';
+import { reduxForm } from 'redux-form';
+import { MORE_SEARCH } from '../../constants';
+import * as MoreAction from '../../actions';
+import { SvgXml } from 'react-native-svg';
+import { MORE } from '../../../../assets/svg';
+import { getTitleByLanguage } from '../../../../navigation/actions';
+
+const mapStateToProps = ({ more, global }) => ({
+ loading: more.loading.logoutLoading,
+ language: global.language
+});
+
+const mapDispatchToProps = {
+ logout: MoreAction.logout
+};
+
+// Redux Forms
+const moreSearchReduxForm = reduxForm({
+ form: MORE_SEARCH,
+})(More);
+
+// connect
+const MoreContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(moreSearchReduxForm);
+
+MoreContainer.navigationOptions = () => ({
+ gesturesEnabled: false,
+ tabBarLabel: getTitleByLanguage('tabNavigation.more'),
+ tabBarIcon: ({ focused }: { focused: boolean }) => (
+
+ ),
+});
+
+export default MoreContainer;
diff --git a/src/features/more/containers/Report/index.js b/src/features/more/containers/Report/index.js
new file mode 100644
index 00000000..b1abe199
--- /dev/null
+++ b/src/features/more/containers/Report/index.js
@@ -0,0 +1,53 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Report } from '../../components/Report';
+import { reduxForm, getFormValues } from 'redux-form';
+import { validate } from './validation';
+import * as MoreAction from '../../actions';
+import { REPORT_FORM, DATE_RANGE } from '../../constants';
+
+const mapStateToProps = (state, { navigation }) => {
+ const {
+ more: { loading },
+ global: { language, company, fiscalYear = '2-1' },
+ } = state;
+
+ const type = navigation.getParam('type');
+
+ const isLoading = loading.reportsLoading || !type
+
+ return {
+ loading: isLoading,
+ formValues: getFormValues(REPORT_FORM)(state) || {},
+ language,
+ type,
+ company,
+ fiscalYear,
+ initialValues: !isLoading && {
+ date_range: DATE_RANGE.THIS_MONTH,
+ report_type: 'byCustomer'
+ },
+ };
+};
+
+const mapDispatchToProps = {
+ generateReport: MoreAction.generateReport,
+};
+
+// Redux Forms
+const ReportReduxForm = reduxForm({
+ form: REPORT_FORM,
+ validate,
+})(Report);
+
+// connect
+const ReportContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(ReportReduxForm);
+
+ReportContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default ReportContainer;
diff --git a/src/features/more/containers/Report/validation.js b/src/features/more/containers/Report/validation.js
new file mode 100644
index 00000000..3da510d8
--- /dev/null
+++ b/src/features/more/containers/Report/validation.js
@@ -0,0 +1,34 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const {
+ from_date,
+ to_date,
+ date_range
+ } = values;
+
+
+ errors.date_range = getError(
+ date_range,
+ ['requiredField'],
+ options = { fieldName: 'Date Range' }
+ );
+
+ errors.from_date = getError(
+ from_date,
+ ['requiredField'],
+ options = { fieldName: 'From Date' }
+ );
+
+ errors.to_date = getError(
+ to_date,
+ ['requiredField'],
+ options = { fieldName: 'To Date' }
+ );
+
+ return errors;
+};
diff --git a/src/features/more/containers/Reports/index.js b/src/features/more/containers/Reports/index.js
new file mode 100644
index 00000000..753bb5b2
--- /dev/null
+++ b/src/features/more/containers/Reports/index.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { Reports } from '../../components/Reports';
+import { colors } from '../../../../styles/colors';
+import { reduxForm } from 'redux-form';
+import { REPORTS_SEARCH } from '../../constants';
+import * as SettingAction from '../../actions';
+
+const mapStateToProps = ({ more, global }) => ({
+ language: global.language
+});
+
+const mapDispatchToProps = {
+ // logout: SettingAction.logout
+};
+
+// Redux Forms
+const reportSearchReduxForm = reduxForm({
+ form: REPORTS_SEARCH,
+})(Reports);
+
+// connect
+const SettingContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(reportSearchReduxForm);
+
+SettingContainer.navigationOptions = () => ({
+ header: null
+});
+
+export default SettingContainer;
diff --git a/src/features/more/reducers/index.js b/src/features/more/reducers/index.js
new file mode 100644
index 00000000..f90cccf0
--- /dev/null
+++ b/src/features/more/reducers/index.js
@@ -0,0 +1,71 @@
+import {
+ MORE_TRIGGER_SPINNER,
+ SET_ITEMS,
+ CLEAR_ITEM,
+ SET_ITEM,
+ DELETE_ITEM,
+ SET_FILTER_ITEMS,
+} from '../constants';
+import { env } from '../../../config';
+
+const initialState = {
+ loading: {
+ logoutLoading: false,
+ itemsLoading: false,
+ itemLoading: false,
+ },
+ items: [],
+ filterItems: [],
+ item: null
+};
+
+export default function moreReducer(state = initialState, action) {
+ const { payload, type } = action;
+
+ switch (type) {
+ case MORE_TRIGGER_SPINNER:
+ return { ...state, loading: { ...state.loading, ...payload } };
+
+ case SET_ITEMS:
+
+ const { items, fresh, prepend } = payload;
+
+ if (prepend) {
+ return { ...state, items: [...items, ...state.items] };
+ }
+
+ if (!fresh) {
+ return { ...state, items: [...state.items, ...items] };
+ }
+
+ return { ...state, items };
+
+ case SET_FILTER_ITEMS:
+
+ if (!payload.fresh) {
+ return {
+ ...state,
+ filterItems: [...state.filterItems, ...payload.items]
+ };
+ }
+
+ return { ...state, filterItems: payload.items };
+
+
+ case DELETE_ITEM:
+ const { id } = payload
+
+ const remainItems = state.items.filter(val => val.id !== id)
+
+ return { ...state, items: remainItems };
+
+ case CLEAR_ITEM:
+ return { ...state, item: null };
+
+ case SET_ITEM:
+ return { ...state, item: payload.item };
+
+ default:
+ return state;
+ }
+}
diff --git a/src/features/more/saga/index.js b/src/features/more/saga/index.js
new file mode 100644
index 00000000..1b0039cd
--- /dev/null
+++ b/src/features/more/saga/index.js
@@ -0,0 +1,227 @@
+import { call, put, takeEvery, takeLatest } from 'redux-saga/effects';
+
+import {
+ moreTriggerSpinner,
+ setItems,
+ setItem,
+ deleteItem,
+ setFilterItems,
+} from '../actions';
+import {
+ LOGOUT,
+ GET_ITEMS,
+ ITEM_ADD,
+ GET_EDIT_ITEM,
+ ITEM_EDIT,
+ REMOVE_ITEM,
+ // Endpoint Api URL
+ GET_ITEMS_URL,
+ GET_EDIT_ITEMS_URL,
+ CREATE_ITEM_URL,
+ EDIT_ITEM_URL,
+ REMOVE_ITEM_URL,
+} from '../constants';
+import Request from '../../../api/request';
+import { resetIdToken } from '../../authentication/actions';
+import { ROUTES } from '../../../navigation/routes';
+
+/**
+ * app logout action.
+ */
+function* logout(payloadData) {
+ const {
+ payload: { navigation },
+ } = payloadData;
+
+ yield put(moreTriggerSpinner({ logoutLoading: true }));
+
+ try {
+ yield put(resetIdToken());
+
+ navigation.navigate(ROUTES.AUTH)
+
+ } catch (error) {
+ alert('something went wrong');
+ } finally {
+ yield put(moreTriggerSpinner({ logoutLoading: false }));
+ }
+}
+
+/**
+ * Global Items.
+ */
+function* getItems(payloadData) {
+ const {
+ payload: {
+ onResult,
+ fresh,
+ onMeta,
+ params = null,
+ pagination: { page = 1, limit = 10 },
+ filter
+ },
+ } = payloadData;
+
+ yield put(moreTriggerSpinner({ itemsLoading: true }));
+
+ try {
+
+ let param = {
+ ...params,
+ page,
+ limit
+ }
+
+ const options = {
+ path: GET_ITEMS_URL(param),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ if (!filter)
+ yield put(setItems({ items: response.items.data, fresh }));
+ else
+ yield put(setFilterItems({ items: response.items.data, fresh }));
+
+ onMeta && onMeta(response.items);
+
+ onResult && onResult(response.items);
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(moreTriggerSpinner({ itemsLoading: false }));
+ }
+}
+
+function* getEditItem(payloadData) {
+ const {
+ payload: { id },
+ } = payloadData;
+
+ yield put(moreTriggerSpinner({ itemLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_EDIT_ITEMS_URL(id),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setItem(response));
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(moreTriggerSpinner({ itemLoading: false }));
+ }
+}
+
+function* addItem(payloadData) {
+ const {
+ payload: {
+ item,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(moreTriggerSpinner({ itemLoading: true }));
+
+ try {
+
+
+ const options = {
+ path: CREATE_ITEM_URL(),
+ body: item
+ };
+
+ const res = yield call([Request, 'post'], options);
+
+ yield put(setItems({ items: [res.item], prepend: true }))
+
+ onResult && onResult()
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(moreTriggerSpinner({ itemLoading: false }));
+ }
+}
+
+function* editItem(payloadData) {
+ const {
+ payload: {
+ item,
+ id,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(moreTriggerSpinner({ itemLoading: true }));
+
+ try {
+
+
+ const options = {
+ path: EDIT_ITEM_URL(id),
+ body: item
+ };
+
+ const response = yield call([Request, 'put'], options);
+
+ yield put(deleteItem({ id }));
+
+ yield put(setItems({ items: [response.item], prepend: true }));
+
+ onResult && onResult()
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(moreTriggerSpinner({ itemLoading: false }));
+ }
+}
+
+function* removeItem(payloadData) {
+ const {
+ payload: {
+ id,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(moreTriggerSpinner({ itemLoading: true }));
+
+ try {
+
+ const options = {
+ path: REMOVE_ITEM_URL(id),
+ };
+
+ const response = yield call([Request, 'delete'], options);
+
+ if (response.success) {
+ yield put(deleteItem({ id }))
+ }
+
+ onResult && onResult(response)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(moreTriggerSpinner({ itemLoading: false }));
+ }
+}
+
+
+export default function* moreSaga() {
+ yield takeEvery(LOGOUT, logout);
+
+ // Items
+ yield takeEvery(GET_ITEMS, getItems);
+ yield takeEvery(ITEM_ADD, addItem);
+ yield takeEvery(ITEM_EDIT, editItem);
+ yield takeEvery(REMOVE_ITEM, removeItem);
+ yield takeEvery(GET_EDIT_ITEM, getEditItem);
+
+}
diff --git a/src/features/payments/actions/index.js b/src/features/payments/actions/index.js
new file mode 100644
index 00000000..9e0b345e
--- /dev/null
+++ b/src/features/payments/actions/index.js
@@ -0,0 +1,66 @@
+
+import {
+ GET_PAYMENTS,
+ SET_PAYMENTS,
+ PAYMENTS_TRIGGER_SPINNER,
+ GET_CREATE_PAYMENT,
+ CREATE_PAYMENT,
+ GET_UNPAID_INVOICES,
+ GET_EDIT_PAYMENT,
+ EDIT_PAYMENT,
+ SET_FILTER_PAYMENTS,
+ REMOVE_PAYMENT,
+} from "../constants";
+
+
+export const getPayments = (payload = {}) => ({
+ type: GET_PAYMENTS,
+ payload,
+});
+
+export const setPayments = (payload = {}) => ({
+ type: SET_PAYMENTS,
+ payload,
+});
+
+export const setFilterPayments = (payload = {}) => ({
+ type: SET_FILTER_PAYMENTS,
+ payload,
+});
+
+export const getCreatePayment = (payload = {}) => ({
+ type: GET_CREATE_PAYMENT,
+ payload,
+});
+
+export const createPayment = (payload = {}) => ({
+ type: CREATE_PAYMENT,
+ payload,
+});
+
+
+export const getUnpaidInvoices = (payload = {}) => ({
+ type: GET_UNPAID_INVOICES,
+ payload,
+});
+
+export const paymentTriggerSpinner = (payload) => ({
+ type: PAYMENTS_TRIGGER_SPINNER,
+ payload,
+});
+
+export const getEditPayment = (payload = {}) => ({
+ type: GET_EDIT_PAYMENT,
+ payload,
+});
+
+
+export const editPayment = (payload = {}) => ({
+ type: EDIT_PAYMENT,
+ payload,
+});
+
+export const removePayment = (payload = {}) => ({
+ type: REMOVE_PAYMENT,
+ payload,
+});
\ No newline at end of file
diff --git a/src/features/payments/components/Payment/index.js b/src/features/payments/components/Payment/index.js
new file mode 100644
index 00000000..df7a9900
--- /dev/null
+++ b/src/features/payments/components/Payment/index.js
@@ -0,0 +1,553 @@
+// @flow
+
+import React from 'react';
+import { View, Alert } from 'react-native';
+import { Field, change } from 'redux-form';
+import moment from 'moment';
+import styles from './styles';
+import {
+ InputField,
+ CtButton,
+ DefaultLayout,
+ DatePickerField,
+ SelectPickerField,
+ SelectField,
+} from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import { DATE_FORMAT } from '../../../../api/consts/core';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { PAYMENT_ADD, PAYMENT_MODE, PAYMENT_EDIT, PAYMENT_FORM, PAYMENT_ACTIONS, ACTIONS_VALUE } from '../../constants';
+import Lng from '../../../../api/lang/i18n';
+import { IMAGES } from '../../../../config';
+import { CUSTOMER_ADD } from '../../../customers/constants';
+import { INVOICES_STATUS_BG_COLOR, INVOICES_STATUS_TEXT_COLOR } from '../../../invoices/constants';
+import { MAX_LENGTH, alertMe } from '../../../../api/global';
+
+let paymentRefs = {}
+
+type IProps = {
+ navigation: Object,
+ customers: Object,
+ getCreatePayment: Function,
+ getEditPayment: Function,
+ getUnpaidInvoices: Function,
+ createPayment: Function,
+ editPayment: Function,
+ handleSubmit: Function,
+ type: String,
+ language: String,
+ paymentLoading: Boolean,
+ initPaymentLoading: Boolean,
+ getUnpaidInvoicesLoading: Boolean,
+ getCustomers: Function,
+}
+
+let editPaymentData = [
+ "payment_date",
+ "payment_number",
+ "user_id",
+ "invoice_id",
+ "payment_mode",
+ "amount",
+ "notes"
+]
+
+export class Payment extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ invoices: [],
+ selectedInvoice: '',
+ selectedCustomer: '',
+ selectedPaymentMode: '',
+ isLoading: true,
+ };
+ }
+
+ componentDidMount() {
+ const {
+ getCreatePayment,
+ navigation,
+ getEditPayment,
+ type,
+ hasRecordPayment,
+ } = this.props;
+
+
+ if (type === PAYMENT_EDIT) {
+
+ let id = navigation.getParam('paymentId', null)
+ this.setFormField('id', id)
+
+ getEditPayment({
+ id,
+ onResult: ({ payment, invoices }) => {
+
+ let { user_id, payment_mode, invoice_id, invoice, amount } = payment
+
+ editPaymentData.map((field) => {
+ this.setFormField(field, payment[field])
+ })
+
+ invoice_id && invoice && this.setFormField('due',
+ (Number(amount) + Number(invoice.due_amount))
+ )
+
+ this.setState({
+ selectedCustomer: user_id ? payment.user : '',
+ selectedInvoice: invoice_id ? payment.invoice.invoice_number : '',
+ selectedPaymentMode: payment_mode,
+ })
+
+ if (user_id)
+ this.setState({ invoices })
+
+ this.setState({ isLoading: false })
+ }
+ });
+ }
+ else {
+ getCreatePayment({
+ onResult: (val) => {
+ this.setFormField('payment_number', val.nextPaymentNumber)
+ this.setFormField('payment_date', moment())
+
+ hasRecordPayment ?
+ this.SetRecordPaymentField() :
+ this.setState({ isLoading: false })
+ }
+ });
+ }
+
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+
+ SetRecordPaymentField = () => {
+ const {
+ invoice: {
+ user,
+ user_id,
+ due_amount,
+ invoice_number,
+ id
+ },
+ getUnpaidInvoices,
+ } = this.props
+
+ this.setFormField('user_id', user_id)
+ this.setFormField('amount', due_amount)
+ this.setFormField('due', due_amount)
+ this.setFormField('invoice_id', id)
+
+ this.setState({
+ selectedCustomer: user,
+ selectedInvoice: invoice_number
+ })
+
+ getUnpaidInvoices({
+ id: user_id,
+ onResult: (invoices) => {
+ invoices.length !== 0 ?
+ this.setState({
+ invoices,
+ isLoading: false
+ })
+ : this.setState({ isLoading: false })
+ }
+ })
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(PAYMENT_FORM, field, value));
+
+ if (field === 'payment_mode') {
+ this.setState({
+ selectedPaymentMode: value
+ })
+ }
+ };
+
+ onCustomerSelect = (customer) => {
+ const { getUnpaidInvoices } = this.props
+ let { id } = customer
+ this.setFormField('user_id', id)
+ this.setState({ selectedCustomer: customer })
+
+ getUnpaidInvoices({
+ id,
+ onResult: (invoices) => {
+ if (invoices.length !== 0) {
+
+ this.setState({
+ invoices,
+ selectedInvoice: ''
+ })
+ this.setFormField('invoice_id', '')
+ }
+ else {
+ this.setState({
+ invoices: [],
+ selectedInvoice: ''
+ })
+ this.setFormField('invoice_id', '')
+ }
+ }
+ })
+ }
+
+ onPaymentSubmit = (values) => {
+
+ const {
+ type,
+ createPayment,
+ editPayment,
+ navigation,
+ language
+ } = this.props
+
+ type === PAYMENT_ADD ?
+ createPayment({
+ params: values,
+ navigation,
+ onResult: (val) => {
+ val === 'invalid_amount' &&
+ alertMe({ title: Lng.t("payments.alertAmount", { locale: language }) })
+ }
+ })
+ :
+ editPayment({
+ id: navigation.getParam('paymentId'),
+ params: values,
+ navigation
+ })
+ };
+
+
+ getInvoicesList = (items) => {
+ let invoicesList = []
+ if (typeof items !== 'undefined' && items.length != 0) {
+
+ const { selectedCustomer } = this.state
+ const { name = '', currency = null } = selectedCustomer
+
+ invoicesList = items.map((item) => {
+ const {
+ invoice_number,
+ status,
+ formattedDueDate,
+ due_amount,
+ } = item;
+
+ return {
+ title: name,
+ subtitle: {
+ title: invoice_number,
+ label: status,
+ labelBgColor: INVOICES_STATUS_BG_COLOR[status],
+ labelTextColor: INVOICES_STATUS_TEXT_COLOR[status],
+ },
+ amount: due_amount,
+ currency,
+ rightSubtitle: formattedDueDate,
+ fullItem: item,
+ };
+ });
+
+ }
+
+ return invoicesList
+ }
+
+ removePayment = () => {
+ const { removePayment, navigation, language } = this.props
+
+ Alert.alert(
+ Lng.t("alert.title", { locale: language }),
+ Lng.t("payments.alertDescription", { locale: language }),
+ [
+ {
+ text: 'OK',
+ onPress: () => removePayment({
+ id: navigation.getParam('paymentId', null),
+ navigation
+ })
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ }
+
+ onOptionSelect = (action) => {
+
+ if (action == ACTIONS_VALUE.REMOVE)
+ this.removePayment()
+
+ }
+
+ BOTTOM_ACTION = (handleSubmit) => {
+
+ const {
+ language,
+ paymentLoading
+ } = this.props
+
+ let buttonTitle = Lng.t("button.save", { locale: language })
+
+ return (
+
+
+
+ )
+ }
+
+ render() {
+ const {
+ navigation,
+ handleSubmit,
+ customers,
+ language,
+ initPaymentLoading,
+ getUnpaidInvoicesLoading,
+ type,
+ getCustomers,
+ formValues: { due = '', amount = 0 },
+ submitFailed = false,
+ } = this.props;
+
+ const {
+ selectedInvoice,
+ selectedCustomer,
+ selectedPaymentMode,
+ invoices,
+ isLoading
+ } = this.state
+
+ let drownDownProps = type === PAYMENT_EDIT ? {
+ options: PAYMENT_ACTIONS(Lng, language),
+ onSelect: this.onOptionSelect,
+ cancelButtonIndex: 1,
+ destructiveButtonIndex: 2
+ } : null
+
+
+ return (
+ navigation.goBack(null),
+ title: type === PAYMENT_EDIT ?
+ Lng.t("header.editPayment", { locale: language }) :
+ Lng.t("header.addPayment", { locale: language }),
+ placement: "center",
+ rightIcon: type !== PAYMENT_EDIT ? "save" : null,
+ rightIconProps: {
+ solid: true,
+ },
+ rightIconPress: handleSubmit(this.onPaymentSubmit),
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ loadingProps={{
+ is: isLoading || initPaymentLoading
+ }}
+ dropdownProps={drownDownProps}
+ >
+
+
+
+
+
+ {
+ this.setFormField('payment_date', val)
+ }}
+ isRequired
+ />
+
+
+
+
+
+
+
+ {
+ this.onCustomerSelect(item)
+ this.setFormField('due', '')
+ }}
+ rightIconPress={
+ () => navigation.navigate(ROUTES.CUSTOMER, {
+ type: CUSTOMER_ADD,
+ onSelect: (val) => {
+ this.onCustomerSelect(val)
+ this.setFormField('due', '')
+ }
+ })
+ }
+ headerProps={{
+ title: Lng.t("customers.title", { locale: language }),
+ }}
+ listViewProps={{
+ hasAvatar: true,
+ }}
+ emptyContentProps={{
+ contentType: "customers",
+ image: IMAGES.EMPTY_CUSTOMERS,
+ }}
+ isRequired
+ isEditable={type === PAYMENT_ADD}
+ fakeInputProps={{
+ disabled: type !== PAYMENT_ADD
+ }}
+ />
+
+ {
+ paymentRefs.amount = ref;
+ }}
+ isRequired
+ />
+
+ {
+ this.setState({ customerList: hasAll ? customers : searchItems })
+ }
+ }
+ onSelect={({ id, due_amount }) => {
+ this.setFormField('invoice_id', id)
+ this.setFormField('amount', due_amount)
+ this.setFormField('due', due_amount)
+ paymentRefs.amount.focus();
+ }}
+ headerProps={{
+ title: Lng.t("invoices.title", { locale: language }),
+ rightIconPress: null
+ }}
+ listViewProps={{
+ }}
+ emptyContentProps={{
+ contentType: "invoices",
+ image: IMAGES.EMPTY_INVOICES,
+ }}
+ containerStyle={
+ due && submitFailed && amount > due &&
+ { marginTop: 22 }
+ }
+ isEditable={type === PAYMENT_ADD}
+ fakeInputProps={{
+ disabled: type !== PAYMENT_ADD
+ }}
+ />
+
+ {
+ this.setFormField('payment_mode', val)
+ }}
+ onDonePress={() => paymentRefs.notes.focus()}
+ defaultPickerOptions={{
+ label: Lng.t("payments.modePlaceholder", { locale: language }),
+ value: '',
+ }}
+ refLinkFn={(ref) => {
+ paymentRefs.mode = ref;
+ }}
+ containerStyle={styles.selectPicker}
+ />
+
+ {
+ paymentRefs.notes = ref;
+ }}
+ />
+
+
+
+ );
+ }
+}
+
diff --git a/src/features/payments/components/Payment/styles.js b/src/features/payments/components/Payment/styles.js
new file mode 100644
index 00000000..d148d11d
--- /dev/null
+++ b/src/features/payments/components/Payment/styles.js
@@ -0,0 +1,48 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+import { isIPhoneX } from '../../../../api/helper';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ selectPicker: {
+ marginTop: 17,
+ },
+ numberDateFieldContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ marginHorizontal: -10,
+ },
+ numberDateField: {
+ flex: 1,
+ paddingHorizontal: 5,
+ justifyContent: 'space-between',
+ },
+ paymentNumberField: {
+ marginVertical: 0,
+ },
+ inBetweenSpace: {
+ paddingHorizontal: 5
+ },
+ submitButton: {
+ paddingHorizontal: 10,
+ },
+ multipleButton: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ flexDirection: 'row'
+ },
+ btnContainerStyle: {
+ marginHorizontal: 10,
+ },
+ flex: {
+ flex: 1
+ }
+});
diff --git a/src/features/payments/components/Payments/index.js b/src/features/payments/components/Payments/index.js
new file mode 100644
index 00000000..5f936667
--- /dev/null
+++ b/src/features/payments/components/Payments/index.js
@@ -0,0 +1,393 @@
+// @flow
+import React from 'react';
+import { View } from 'react-native';
+
+import { change } from 'redux-form';
+import styles from './styles';
+import {
+ MainLayout,
+ ListView
+} from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import { IMAGES } from '../../../../config';
+import Lng from '../../../../api/lang/i18n';
+import { PAYMENT_ADD, PAYMENT_EDIT, PAYMENT_SEARCH, PAYMENT_MODE } from '../../constants';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+
+let params = {
+ search: '',
+ payment_mode: '',
+ payment_number: '',
+ customer_id: '',
+}
+
+type IProps = {
+ navigation: Object,
+ getPayments: Function,
+ payments: Object,
+ loading: Boolean,
+ language: String,
+ getCustomers: Function,
+}
+
+export class Payments extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ refreshing: false,
+ fresh: true,
+ pagination: {
+ page: 1,
+ limit: 10,
+ lastPage: 1,
+ },
+ search: '',
+ selectedPaymentMode: '',
+ filter: false
+ };
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation, ROUTES.MAIN_INVOICES)
+ }
+
+ componentWillUpdate(nextProps, nextState) {
+
+ const { navigation } = nextProps
+ const pagination = navigation.getParam('pagination', null)
+ const apiCall = navigation.getParam('apiCall', false)
+
+ if (pagination && !(apiCall)) {
+ navigation.setParams({ 'pagination': null, apiCall: true })
+
+ const { last_page, current_page } = pagination
+ this.setState({
+ pagination: {
+ ...this.state.pagination,
+ lastPage: last_page,
+ page: current_page + 1,
+ }
+ });
+ }
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ onPaymentSelect = (payment) => {
+ const { navigation } = this.props
+ navigation.navigate(ROUTES.PAYMENT,
+ { paymentId: payment.id, type: PAYMENT_EDIT }
+ )
+ this.onResetFilter()
+ }
+
+ onSearch = (search) => {
+ this.onResetFilter()
+ this.setState({ search })
+ this.getItems({ fresh: true, params: { ...params, search } })
+ };
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(PAYMENT_SEARCH, field, value));
+
+ if (field === 'payment_mode')
+ this.setState({ selectedPaymentMode: value })
+ };
+
+ getItems = ({
+ fresh = false,
+ onResult,
+ params,
+ filter = false
+ } = {}) => {
+
+ const { getPayments } = this.props;
+ const { refreshing, pagination } = this.state;
+
+ if (refreshing) {
+ return;
+ }
+
+ this.setState({
+ refreshing: true,
+ fresh,
+ });
+
+ const paginationParams = fresh ? { ...pagination, page: 1 } : pagination;
+
+ if (!fresh && paginationParams.lastPage < paginationParams.page) {
+ return;
+ }
+
+ getPayments({
+ fresh,
+ pagination: paginationParams,
+ params,
+ filter,
+ onMeta: ({ last_page, current_page }) => {
+ this.setState({
+ pagination: {
+ ...paginationParams,
+ lastPage: last_page,
+ page: current_page + 1,
+ },
+ });
+ },
+ onResult: (val) => {
+ this.setState({
+ refreshing: false,
+ fresh: !val,
+ });
+ onResult && onResult();
+ },
+ });
+ };
+
+ onResetFilter = () => {
+ this.setState({ filter: false })
+ }
+
+ onSubmitFilter = ({ customer_id = '', payment_mode = '', payment_number = '' }) => {
+
+ if (customer_id || payment_mode || payment_number) {
+ this.setState({ filter: true })
+
+ this.getItems({
+ fresh: true,
+ params: {
+ ...params,
+ customer_id,
+ payment_mode,
+ payment_number,
+ },
+ filter: true
+ })
+ }
+ else
+ this.onResetFilter()
+ }
+
+ getPaymentsList = (payments) => {
+ let paymentList = []
+ if (typeof payments !== 'undefined' && payments.length != 0) {
+ paymentList = payments.map((payment) => {
+ const {
+ notes,
+ formattedPaymentDate,
+ amount,
+ payment_mode,
+ user: { name, currency }
+ } = payment;
+
+ return {
+ title: `${name}`,
+ subtitle: {
+ title: `${payment_mode ? '(' + payment_mode + ')' : ''}`,
+ },
+ amount,
+ currency,
+ rightSubtitle: formattedPaymentDate,
+ fullItem: payment,
+ };
+ });
+ }
+ return paymentList
+ }
+
+ loadMoreItems = () => {
+ const { search, filter } = this.state
+
+ const {
+ formValues: {
+ customer_id = '',
+ payment_mode = '',
+ payment_number = ''
+ }
+ } = this.props
+
+
+
+ if (filter) {
+ this.getItems({
+ params: {
+ ...params,
+ customer_id,
+ payment_mode,
+ payment_number,
+ },
+ filter: true
+ })
+ }
+ else
+ this.getItems({ params: { ...params, search } });
+
+ }
+
+ render() {
+
+ const {
+ navigation,
+ payments,
+ filterPayments,
+ loading,
+ language,
+ handleSubmit,
+ customers,
+ getCustomers
+ } = this.props;
+
+ const {
+ refreshing,
+ pagination: { lastPage, page },
+ fresh,
+ search,
+ selectedPaymentMode,
+ filter,
+ } = this.state;
+
+ const canLoadMore = lastPage >= page;
+
+
+ let paymentsItem = this.getPaymentsList(payments)
+ let filterPaymentItem = this.getPaymentsList(filterPayments)
+
+ let filterRefs = {}
+
+ let selectFields = [
+ {
+ name: "customer_id",
+ apiSearch: true,
+ hasPagination: true,
+ getItems: getCustomers,
+ items: customers,
+ displayName: "name",
+ label: Lng.t("payments.customer", { locale: language }),
+ icon: 'user',
+ placeholder: Lng.t("customers.placeholder", { locale: language }),
+ navigation: navigation,
+ compareField: "id",
+ onSelect: (item) => this.setFormField('customer_id', item.id),
+ headerProps: {
+ title: Lng.t("customers.title", { locale: language }),
+ rightIconPress: null
+ },
+ listViewProps: {
+ hasAvatar: true,
+ },
+ emptyContentProps: {
+ contentType: "customers",
+ image: IMAGES.EMPTY_CUSTOMERS,
+ }
+ }
+ ]
+
+ let inputFields = [{
+ name: 'payment_number',
+ hint: Lng.t("payments.number", { locale: language }),
+ leftIcon: 'hashtag',
+ inputProps: {
+ autoCapitalize: 'none',
+ autoCorrect: true,
+ },
+ refLinkFn: (ref) => {
+ filterRefs.paymentNumber = ref;
+ }
+ }]
+
+ let dropdownFields = [{
+ name: "payment_mode",
+ label: Lng.t("payments.mode", { locale: language }),
+ fieldIcon: 'align-center',
+ items: PAYMENT_MODE,
+ onChangeCallback: (val) => {
+ this.setFormField('payment_mode', val)
+ },
+ defaultPickerOptions: {
+ label: Lng.t("payments.modePlaceholder", { locale: language }),
+ value: '',
+ },
+ selectedItem: selectedPaymentMode,
+ onDonePress: () => filterRefs.paymentNumber.focus(),
+ containerStyle: styles.selectPicker
+ }]
+
+ let empty = (!filter && !search) ? {
+ description: Lng.t("payments.empty.description", { locale: language }),
+ buttonTitle: Lng.t("payments.empty.buttonTitle", { locale: language }),
+ buttonPress: () => {
+ navigation.navigate(ROUTES.PAYMENT, { type: PAYMENT_ADD })
+ this.onResetFilter()
+ }
+ } : {}
+
+ let emptyTitle = search ? Lng.t("search.noResult", { locale: language, search })
+ : (!filter) ? Lng.t("payments.empty.title", { locale: language }) :
+ Lng.t("filter.empty.filterTitle", { locale: language })
+
+ let isLoading = navigation.getParam('loading', false)
+
+ return (
+
+ {
+ navigation.navigate(ROUTES.PAYMENT, { type: PAYMENT_ADD })
+ this.onResetFilter()
+ },
+ title: Lng.t("header.payments", { locale: language })
+ }}
+ onSearch={this.onSearch}
+ bottomDivider
+ filter
+ filterProps={{
+ onSubmitFilter: handleSubmit(this.onSubmitFilter),
+ selectFields: selectFields,
+ inputFields: inputFields,
+ dropdownFields: dropdownFields,
+ clearFilter: this.props,
+ language: language
+ }}
+ loadingProps={{ is: isLoading || (loading && fresh) }}
+ >
+
+
+
+ {
+ this.onResetFilter()
+ this.getItems({
+ fresh: true,
+ onResult: onHide,
+ params: { ...params, search }
+ });
+ }}
+ getItems={() => {
+ this.loadMoreItems()
+ }}
+ contentContainerStyle={{ flex: 0 }}
+ bottomDivider
+ emptyContentProps={{
+ title: emptyTitle,
+ image: IMAGES.EMPTY_PAYMENTS,
+ ...empty
+ }}
+ />
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/payments/components/Payments/styles.js b/src/features/payments/components/Payments/styles.js
new file mode 100644
index 00000000..58c57340
--- /dev/null
+++ b/src/features/payments/components/Payments/styles.js
@@ -0,0 +1,19 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ listViewContainer: {
+ flex: 1,
+ },
+ selectPicker: {
+ marginTop: 12,
+ marginBottom: 5,
+ },
+});
diff --git a/src/features/payments/constants.js b/src/features/payments/constants.js
new file mode 100644
index 00000000..896d2902
--- /dev/null
+++ b/src/features/payments/constants.js
@@ -0,0 +1,65 @@
+import queryString from 'query-string';
+
+// Forms
+// -----------------------------------------
+export const PAYMENT_SEARCH = 'payments/PAYMENT_SEARCH';
+export const PAYMENT_FORM = 'payments/PAYMENT_FORM';
+
+// Type
+// -----------------------------------------
+export const PAYMENT_ADD = 'payments/PAYMENT_ADD';
+export const PAYMENT_EDIT = 'payments/PAYMENT_EDIT';
+
+// Actions
+// -----------------------------------------
+export const GET_PAYMENTS = 'payments/GET_PAYMENTS';
+export const SET_PAYMENTS = 'payments/SET_PAYMENTS';
+export const SET_FILTER_PAYMENTS = 'payments/SET_FILTER_PAYMENTS';
+
+export const GET_CREATE_PAYMENT = 'payments/GET_CREATE_PAYMENT';
+export const CREATE_PAYMENT = 'payments/CREATE_PAYMENT';
+export const GET_UNPAID_INVOICES = 'payments/GET_UNPAID_INVOICES';
+export const PAYMENTS_TRIGGER_SPINNER = 'payments/PAYMENTS_TRIGGER_SPINNER';
+export const GET_EDIT_PAYMENT = 'payments/GET_EDIT_PAYMENT';
+export const EDIT_PAYMENT = 'payments/EDIT_PAYMENT';
+export const REMOVE_PAYMENT = 'payments/REMOVE_PAYMENT';
+
+// Payment Mode
+// -----------------------------------------
+export const PAYMENT_MODE = [
+ { label: 'Cash', value: 'Cash' },
+ { label: 'Check', value: 'Check' },
+ { label: 'Credit Card', value: 'Credit Card' },
+ { label: 'Bank Transfer', value: 'Bank Transfer' }
+]
+
+export const ACTIONS_VALUE = {
+ REMOVE: 'remove',
+}
+
+export const PAYMENT_ACTIONS = (Lng, language) => {
+ return [
+ {
+ label: Lng.t("payments.removePayment", { locale: language })
+ ,
+ value: ACTIONS_VALUE.REMOVE
+ }
+ ];
+}
+
+// Endpoint Api URL
+// -----------------------------------------
+
+export const GET_PAYMENTS_URL = (param) => `payments?${queryString.stringify({
+ ...param,
+ orderByField: 'created_at',
+ orderBy: 'desc'
+})}`
+
+export const CREATE_PAYMENT_URL = () => `payments`
+export const EDIT_PAYMENT_URL = (id) => `payments/${id}`
+export const REMOVE_PAYMENT_URL = (id) => `payments/${id}`
+
+export const GET_EDIT_PAYMENT_URL = (id) => `payments/${id}/edit`
+export const GET_CREATE_PAYMENTS_URL = () => `payments/create`
+export const GET_UNPAID_INVOICES_URL = (id) => `invoices/unpaid/${id}`
\ No newline at end of file
diff --git a/src/features/payments/containers/Payment/index.js b/src/features/payments/containers/Payment/index.js
new file mode 100644
index 00000000..6bd6e17b
--- /dev/null
+++ b/src/features/payments/containers/Payment/index.js
@@ -0,0 +1,73 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { reduxForm, getFormValues } from 'redux-form';
+import { validate } from './validation';
+import * as PaymentAction from '../../actions';
+import { PAYMENT_FORM, PAYMENT_ADD } from '../../constants';
+import { Payment } from '../../components/Payment';
+import { getCustomers } from '../../../customers/actions';
+
+const mapStateToProps = (state, { navigation }) => {
+
+ const {
+ customers: { customers },
+ global: { language },
+ payments: {
+ loading: {
+ initPaymentLoading,
+ paymentLoading,
+ getUnpaidInvoicesLoading,
+ }
+ }
+ } = state
+
+ let type = navigation.getParam('type', PAYMENT_ADD)
+ let invoice = navigation.getParam('invoice', null)
+ let hasRecordPayment = navigation.getParam('hasRecordPayment', false)
+
+ return {
+ type,
+ customers,
+ language: language,
+ invoice,
+ hasRecordPayment,
+ initPaymentLoading,
+ paymentLoading,
+ getUnpaidInvoicesLoading,
+ formValues: getFormValues(PAYMENT_FORM)(state) || {},
+
+ initialValues: {
+ payment_mode: null
+ }
+ };
+
+};
+
+const mapDispatchToProps = {
+ getCreatePayment: PaymentAction.getCreatePayment,
+ createPayment: PaymentAction.createPayment,
+ getEditPayment: PaymentAction.getEditPayment,
+ getUnpaidInvoices: PaymentAction.getUnpaidInvoices,
+ editPayment: PaymentAction.editPayment,
+ removePayment: PaymentAction.removePayment,
+ getCustomers: getCustomers
+};
+
+
+// Redux Forms
+const addEditPaymentReduxForm = reduxForm({
+ form: PAYMENT_FORM,
+ validate: (val) => validate(val),
+})(Payment);
+
+// connect
+const AddEditPaymentContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(addEditPaymentReduxForm);
+
+AddEditPaymentContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default AddEditPaymentContainer;
diff --git a/src/features/payments/containers/Payment/validation.js b/src/features/payments/containers/Payment/validation.js
new file mode 100644
index 00000000..eb24e842
--- /dev/null
+++ b/src/features/payments/containers/Payment/validation.js
@@ -0,0 +1,36 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const {
+ payment_date,
+ payment_number,
+ user_id,
+ amount,
+ due,
+ } = values;
+
+ errors.payment_date = getError(payment_date, ['required']);
+ errors.payment_number = getError(payment_number, ['required']);
+
+ errors.user_id = getError(
+ user_id,
+ ['requiredField'],
+ );
+
+ errors.amount = getError(
+ amount,
+ ['requiredField', 'isNumberFormat'],
+ );
+
+ if (amount > due)
+ errors.amount = getError(
+ amount,
+ ['moreThanDue'],
+ );
+
+ return errors;
+};
diff --git a/src/features/payments/containers/Payments/index.js b/src/features/payments/containers/Payments/index.js
new file mode 100644
index 00000000..5bd58702
--- /dev/null
+++ b/src/features/payments/containers/Payments/index.js
@@ -0,0 +1,78 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { reduxForm, getFormValues } from 'redux-form';
+import * as PaymentsAction from '../../actions';
+import { colors } from '../../../../styles/colors';
+import { Payments } from '../../components/Payments';
+import { PAYMENT_SEARCH } from '../../constants';
+import { SvgXml } from 'react-native-svg';
+import { PAYMETNS } from '../../../../assets/svg';
+import { getCustomers } from '../../../customers/actions';
+import { getTitleByLanguage, tabBarOnPress, navigateTabRoutes, navigateRoute } from '../../../../navigation/actions';
+import { ROUTES } from '../../../../navigation/routes';
+
+
+const mapStateToProps = (state) => {
+
+ const {
+ global: { language },
+ customers: { customers },
+ payments: {
+ payments,
+ filterPayments,
+ loading: { paymentsLoading }
+ }
+ } = state;
+
+ return {
+ payments,
+ filterPayments,
+ loading: paymentsLoading,
+ language,
+ customers,
+ formValues: getFormValues(PAYMENT_SEARCH)(state) || {},
+ };
+};
+
+
+const mapDispatchToProps = {
+ getPayments: PaymentsAction.getPayments,
+ getCustomers: getCustomers
+};
+
+// Redux Forms
+const paymentSearchReduxForm = reduxForm({
+ form: PAYMENT_SEARCH,
+})(Payments);
+
+// connect
+const PaymentsContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(paymentSearchReduxForm);
+
+PaymentsContainer.navigationOptions = ({ navigation }) => ({
+ gesturesEnabled: false,
+ tabBarLabel: getTitleByLanguage('tabNavigation.payments'),
+ tabBarIcon: ({ focused }: { focused: boolean }) => (
+
+ ),
+ tabBarOnPress: () => {
+
+ navigateTabRoutes(ROUTES.MAIN_PAYMENTS, { apiCall: false })
+
+ let apiCall = navigation.getParam('apiCall', false)
+
+ apiCall ? navigateRoute(ROUTES.MAIN_PAYMENTS) : tabBarOnPress(
+ ROUTES.MAIN_PAYMENTS,
+ PaymentsAction.getPayments
+ )
+ }
+});
+
+export default PaymentsContainer;
diff --git a/src/features/payments/reducers/index.js b/src/features/payments/reducers/index.js
new file mode 100644
index 00000000..921419db
--- /dev/null
+++ b/src/features/payments/reducers/index.js
@@ -0,0 +1,51 @@
+import {
+ SET_PAYMENTS,
+ PAYMENTS_TRIGGER_SPINNER,
+ SET_FILTER_PAYMENTS,
+} from "../constants";
+
+const initialState = {
+ payments: [],
+ filterPayments: [],
+ errors: null,
+ loading: {
+ paymentsLoading: false,
+ initPaymentLoading: false,
+ paymentLoading: false,
+ getUnpaidInvoicesLoading: false,
+ },
+};
+
+export default function paymentsReducer(state = initialState, action) {
+ const { payload, type } = action;
+
+ switch (type) {
+
+ case SET_PAYMENTS:
+
+ let { payments, fresh } = payload;
+
+ if (!fresh) {
+ return { ...state, payments: [...state.payments, ...payments] };
+ }
+
+ return { ...state, payments };
+
+ case SET_FILTER_PAYMENTS:
+
+ if (!payload.fresh) {
+ return {
+ ...state,
+ filterPayments: [...state.filterPayments, ...payload.payments]
+ };
+ }
+
+ return { ...state, filterPayments: payload.payments };
+
+ case PAYMENTS_TRIGGER_SPINNER:
+ return { ...state, loading: { ...payload } };
+
+ default:
+ return state;
+ }
+}
diff --git a/src/features/payments/saga/index.js b/src/features/payments/saga/index.js
new file mode 100644
index 00000000..6d38e1e6
--- /dev/null
+++ b/src/features/payments/saga/index.js
@@ -0,0 +1,234 @@
+import { call, put, takeEvery } from 'redux-saga/effects';
+import Request from '../../../api/request';
+import {
+ GET_PAYMENTS,
+ GET_CREATE_PAYMENT,
+ CREATE_PAYMENT,
+ GET_UNPAID_INVOICES,
+ GET_EDIT_PAYMENT,
+ EDIT_PAYMENT,
+ REMOVE_PAYMENT,
+ GET_PAYMENTS_URL,
+ // Endpoint Api URL
+ GET_CREATE_PAYMENTS_URL,
+ CREATE_PAYMENT_URL,
+ GET_UNPAID_INVOICES_URL,
+ GET_EDIT_PAYMENT_URL,
+ EDIT_PAYMENT_URL,
+ REMOVE_PAYMENT_URL
+} from '../constants';
+
+import {
+ paymentTriggerSpinner,
+ setPayments,
+ setFilterPayments,
+} from '../actions';
+import { ROUTES } from '../../../navigation/routes';
+
+
+
+function* getPayments(payloadData) {
+
+ const {
+ payload: {
+ onResult = null,
+ onMeta = null,
+ fresh = true,
+ params = null,
+ filter = false,
+ pagination: { page = 1, limit = 10 } = {},
+ } = {},
+ } = payloadData;
+
+ yield put(paymentTriggerSpinner({ paymentsLoading: true }));
+
+ try {
+
+ let param = {
+ ...params,
+ page,
+ limit
+ }
+ const options = {
+ path: GET_PAYMENTS_URL(param),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ if (!filter)
+ yield put(setPayments({ payments: response.payments.data, fresh }));
+ else
+ yield put(setFilterPayments({ payments: response.payments.data, fresh }));
+
+ onMeta && onMeta(response.payments);
+
+ onResult && onResult(true);
+ } catch (error) {
+ onResult && onResult(false);
+ } finally {
+ yield put(paymentTriggerSpinner({ paymentsLoading: false }));
+ }
+}
+
+function* getCreatePayment(payloadData) {
+ const {
+ payload: { onResult },
+ } = payloadData;
+
+ yield put(paymentTriggerSpinner({ initPaymentLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_CREATE_PAYMENTS_URL(),
+ };
+
+ const response = yield call([Request, 'get'], options);
+ onResult && onResult(response);
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(paymentTriggerSpinner({ initPaymentLoading: false }));
+ }
+}
+
+
+function* createPayment(payloadData) {
+ const {
+ payload: { params, navigation, onResult },
+ } = payloadData;
+ yield put(paymentTriggerSpinner({ paymentLoading: true }));
+
+ try {
+
+ const options = {
+ path: CREATE_PAYMENT_URL(),
+ body: params
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ if (response.success) {
+ navigation.navigate(ROUTES.MAIN_PAYMENTS)
+ yield call(getPayments, payload = {});
+ } else {
+ onResult && onResult(response.error)
+ }
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(paymentTriggerSpinner({ paymentLoading: false }));
+ }
+}
+
+function* getUnpaidInvoices(payloadData) {
+ const {
+ payload: { onResult, id },
+ } = payloadData;
+
+ yield put(paymentTriggerSpinner({ getUnpaidInvoicesLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_UNPAID_INVOICES_URL(id),
+ };
+
+ const response = yield call([Request, 'get'], options);
+ onResult && onResult(response.invoices);
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(paymentTriggerSpinner({ getUnpaidInvoicesLoading: false }));
+ }
+}
+
+function* getEditPayment(payloadData) {
+ const {
+ payload: { id, onResult },
+ } = payloadData;
+
+ yield put(paymentTriggerSpinner({ initPaymentLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_EDIT_PAYMENT_URL(id),
+ };
+
+ const response = yield call([Request, 'get'], options);
+ onResult && onResult(response)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(paymentTriggerSpinner({ initPaymentLoading: false }));
+ }
+}
+
+
+function* editPayment(payloadData) {
+ const {
+ payload: { id, params, navigation },
+ } = payloadData;
+
+
+ yield put(paymentTriggerSpinner({ paymentLoading: true }));
+
+ try {
+
+ const options = {
+ path: EDIT_PAYMENT_URL(id),
+ body: params
+ };
+
+ const response = yield call([Request, 'put'], options);
+ navigation.navigate(ROUTES.MAIN_PAYMENTS)
+ yield call(getPayments, payload = {});
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(paymentTriggerSpinner({ paymentLoading: false }));
+ }
+}
+
+function* removePayment(payloadData) {
+ const {
+ payload: { id, navigation },
+ } = payloadData;
+
+ yield put(paymentTriggerSpinner({ paymentLoading: true }));
+
+ try {
+
+ const options = {
+ path: REMOVE_PAYMENT_URL(id),
+ };
+
+ const response = yield call([Request, 'delete'], options);
+
+ if (response.success) {
+ navigation.navigate(ROUTES.MAIN_PAYMENTS)
+ yield call(getPayments, payload = {});
+ }
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(paymentTriggerSpinner({ paymentLoading: false }));
+ }
+}
+
+export default function* paymentsSaga() {
+ yield takeEvery(GET_PAYMENTS, getPayments);
+ yield takeEvery(GET_CREATE_PAYMENT, getCreatePayment);
+ yield takeEvery(CREATE_PAYMENT, createPayment);
+ yield takeEvery(GET_UNPAID_INVOICES, getUnpaidInvoices);
+ yield takeEvery(GET_EDIT_PAYMENT, getEditPayment);
+ yield takeEvery(EDIT_PAYMENT, editPayment);
+ yield takeEvery(REMOVE_PAYMENT, removePayment);
+}
diff --git a/src/features/settings/actions/index.js b/src/features/settings/actions/index.js
new file mode 100644
index 00000000..530464a1
--- /dev/null
+++ b/src/features/settings/actions/index.js
@@ -0,0 +1,202 @@
+import {
+ SETTINGS_TRIGGER_SPINNER,
+ LOGOUT,
+ GET_COMPANY_INFO,
+ EDIT_COMPANY_INFO,
+ GET_ACCOUNT_INFO,
+ EDIT_ACCOUNT_INFO,
+ SET_COMPANY_INFO,
+ SET_ACCOUNT_INFO,
+ GET_PREFERENCES,
+ EDIT_PREFERENCES,
+ SET_PREFERENCES,
+ CLEAR_PREFERENCES,
+ GET_SETTING_ITEM,
+ EDIT_SETTING_ITEM,
+ GET_EXPENSE_CATEGORIES,
+ GET_CREATE_EXPENSE_CATEGORY,
+ CREATE_EXPENSE_CATEGORY,
+ EDIT_EXPENSE_CATEGORY,
+ REMOVE_EXPENSE_CATEGORY,
+ SET_EXPENSE_CATEGORIES,
+ SET_CREATE_EXPENSE_CATEGORIES,
+ SET_EDI_EXPENSE_CATEGORIES,
+ SET_REMOVE_EXPENSE_CATEGORIES,
+ GET_TAXES,
+ SET_TAXES,
+ REMOVE_TAX,
+ SET_TAX,
+ TAX_ADD,
+ TAX_EDIT,
+ SET_EDIT_TAX,
+ SET_REMOVE_TAX,
+} from "../constants";
+
+import { SET_SETTINGS } from "../../../api/consts";
+
+export const settingsTriggerSpinner = (payload) => ({
+ type: SETTINGS_TRIGGER_SPINNER,
+ payload,
+});
+
+export const logout = (payload) => ({
+ type: LOGOUT,
+ payload,
+});
+
+// company
+export const getCompanyInformation = (payload) => ({
+ type: GET_COMPANY_INFO,
+ payload,
+});
+
+export const setCompanyInformation = (payload) => ({
+ type: SET_COMPANY_INFO,
+ payload,
+});
+
+export const editCompanyInformation = (payload) => ({
+ type: EDIT_COMPANY_INFO,
+ payload,
+});
+
+// account
+export const getAccountInformation = (payload) => ({
+ type: GET_ACCOUNT_INFO,
+ payload,
+});
+
+export const setAccountInformation = (payload) => ({
+ type: SET_ACCOUNT_INFO,
+ payload,
+});
+
+export const editAccountInformation = (payload) => ({
+ type: EDIT_ACCOUNT_INFO,
+ payload,
+});
+
+// preferences
+export const getPreferences = (payload) => ({
+ type: GET_PREFERENCES,
+ payload,
+});
+
+export const setPreferences = (payload) => ({
+ type: SET_PREFERENCES,
+ payload,
+});
+
+export const clearPreferences = (payload) => ({
+ type: CLEAR_PREFERENCES,
+ payload,
+});
+
+export const editPreferences = (payload) => ({
+ type: EDIT_PREFERENCES,
+ payload,
+});
+
+// Settings
+
+export const getSettingItem = (payload) => ({
+ type: GET_SETTING_ITEM,
+ payload,
+});
+
+export const editSettingItem = (payload) => ({
+ type: EDIT_SETTING_ITEM,
+ payload,
+});
+
+export const setSettings = (payload) => ({
+ type: SET_SETTINGS,
+ payload,
+});
+
+// Expense Categories
+export const getExpenseCategories = (payload) => ({
+ type: GET_EXPENSE_CATEGORIES,
+ payload,
+});
+
+export const setExpenseCategories = (payload) => ({
+ type: SET_EXPENSE_CATEGORIES,
+ payload,
+});
+
+export const setCreateExpenseCategories = (payload) => ({
+ type: SET_CREATE_EXPENSE_CATEGORIES,
+ payload,
+});
+
+export const setEditExpenseCategories = (payload) => ({
+ type: SET_EDI_EXPENSE_CATEGORIES,
+ payload,
+});
+
+export const setRemoveExpenseCategories = (payload) => ({
+ type: SET_REMOVE_EXPENSE_CATEGORIES,
+ payload,
+});
+
+export const createExpenseCategory = (payload = {}) => ({
+ type: CREATE_EXPENSE_CATEGORY,
+ payload,
+});
+
+export const getEditExpenseCategory = (payload = {}) => ({
+ type: GET_CREATE_EXPENSE_CATEGORY,
+ payload,
+});
+
+export const removeExpenseCategory = (payload = {}) => ({
+ type: REMOVE_EXPENSE_CATEGORY,
+ payload,
+});
+
+export const editExpenseCategory = (payload = {}) => ({
+ type: EDIT_EXPENSE_CATEGORY,
+ payload,
+});
+
+// Taxes
+export const getTaxes = (payload) => ({
+ type: GET_TAXES,
+ payload,
+});
+
+export const setTaxes = (payload) => ({
+ type: SET_TAXES,
+ payload,
+});
+
+export const addTax = (payload) => ({
+ type: TAX_ADD,
+ payload,
+});
+
+export const editTax = (payload) => ({
+ type: TAX_EDIT,
+ payload,
+});
+
+export const removeTax = (payload) => ({
+ type: REMOVE_TAX,
+ payload,
+});
+
+export const setTax = (payload) => ({
+ type: SET_TAX,
+ payload,
+});
+
+export const setEditTax = (payload) => ({
+ type: SET_EDIT_TAX,
+ payload,
+});
+
+export const setRemoveTax = (payload) => ({
+ type: SET_REMOVE_TAX,
+ payload,
+});
\ No newline at end of file
diff --git a/src/features/settings/components/Account/index.js b/src/features/settings/components/Account/index.js
new file mode 100644
index 00000000..d026f20f
--- /dev/null
+++ b/src/features/settings/components/Account/index.js
@@ -0,0 +1,173 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import styles from './styles';
+import { DefaultLayout, CtButton, InputField } from '../../../../components';
+import { Field, change } from 'redux-form';
+import Lng from '../../../../api/lang/i18n';
+import { EDIT_ACCOUNT } from '../../constants';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { headerTitle } from '../../../../api/helper';
+
+
+let name = 'name'
+let Email = 'email'
+let password = 'password'
+let cpassword = 'confirmPassword'
+
+type IProps = {
+ getAccount: Function,
+ editAccount: Function,
+ navigation: Object,
+ language: String,
+ handleSubmit: Function,
+ isLoading: Boolean,
+ editAccountLoading: Boolean
+}
+export class Account extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+
+ }
+ }
+
+ componentDidMount() {
+ const { getAccount, navigation } = this.props
+ getAccount()
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(EDIT_ACCOUNT, field, value));
+ };
+
+ onProfileUpdate = (value) => {
+ const { navigation, editAccount } = this.props
+ editAccount({ params: value, navigation })
+ }
+
+ BOTTOM_ACTION = (handleSubmit) => {
+ const { editAccountLoading, language } = this.props
+ return (
+
+
+
+ )
+ }
+
+ render() {
+ const {
+ navigation,
+ handleSubmit,
+ language,
+ isLoading,
+ } = this.props;
+
+ let accountRefs = {}
+
+ return (
+ navigation.goBack(null),
+ title: Lng.t("header.setting.account", { locale: language }),
+ titleStyle: headerTitle({ marginLeft: -20, marginRight: -25 }),
+ placement: "center",
+ rightIcon: "save",
+ rightIconProps: {
+ solid: true,
+ },
+ rightIconPress: handleSubmit(this.onProfileUpdate),
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ loadingProps={{
+ is: isLoading
+ }}
+ >
+
+
+ {
+ accountRefs.email.focus();
+ }
+ }}
+ />
+
+ {
+ accountRefs.password.focus();
+ }
+ }}
+ refLinkFn={(ref) => {
+ accountRefs.email = ref;
+ }}
+ />
+
+ {
+ accountRefs.confirm.focus();
+ }
+ }}
+ secureTextEntry
+ secureTextIconContainerStyle={styles.eyeIcon}
+ refLinkFn={(ref) => {
+ accountRefs.password = ref;
+ }}
+ />
+
+ {
+ accountRefs.confirm = ref;
+ }}
+ />
+
+
+
+ );
+ }
+}
diff --git a/src/features/settings/components/Account/styles.js b/src/features/settings/components/Account/styles.js
new file mode 100644
index 00000000..5b0a688b
--- /dev/null
+++ b/src/features/settings/components/Account/styles.js
@@ -0,0 +1,21 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ mainContainer: {
+ paddingHorizontal: 20,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ submitButton: {
+ paddingHorizontal: 10
+ },
+ eyeIcon: {
+ top: 6,
+ }
+});
diff --git a/src/features/settings/components/Categories/index.js b/src/features/settings/components/Categories/index.js
new file mode 100644
index 00000000..54f37807
--- /dev/null
+++ b/src/features/settings/components/Categories/index.js
@@ -0,0 +1,188 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import styles from './styles';
+import {
+ MainLayout,
+ ListView
+} from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import Lng from '../../../../api/lang/i18n';
+import { CATEGORY_ADD, CATEGORY_EDIT } from '../../constants';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+
+
+type IProps = {
+ navigation: Object,
+ getPayments: Function,
+ payments: Object,
+ loading: Boolean,
+ language: String,
+}
+
+export class Categories extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ search: '',
+ categoriesFilter: [],
+ found: true,
+ refreshing: false
+ };
+ }
+
+ componentDidMount() {
+ const { getExpenseCategories, navigation } = this.props
+ getExpenseCategories()
+
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ onSelectCategory = (category) => {
+
+ const { navigation } = this.props
+ navigation.navigate(ROUTES.CATEGORY,
+ { type: CATEGORY_EDIT, categoryId: category.id }
+ )
+ }
+
+ onSearch = (search) => {
+
+ const { categories } = this.props;
+ let searchFields = ['name'];
+
+ if (typeof categories !== 'undefined' && categories.length != 0) {
+
+ let newData = categories.filter((category) => {
+ let filterData = false
+
+ searchFields.filter((field) => {
+ let itemField = category[field] ? category[field] : ''
+
+ if (itemField !== null && itemField !== 'undefined') {
+ itemField = itemField.toLowerCase()
+
+ let searchData = search.toString().toLowerCase()
+
+ if (itemField.indexOf(searchData) > -1) {
+ filterData = true
+ }
+ }
+ })
+ return filterData
+ });
+
+ let categoriesFilter = this.itemList(newData)
+
+ this.setState({
+ categoriesFilter,
+ found: categoriesFilter.length != 0 ? true : false,
+ search
+ })
+ }
+ };
+
+ itemList = (categories) => {
+ let categoriesList = []
+ if (typeof categories !== 'undefined' && categories.length != 0) {
+ categoriesList = categories.map((category) => {
+ const { name, description } = category;
+
+ return {
+ title: name || '',
+ subtitle: {
+ title: description,
+ },
+ fullItem: category,
+ };
+ });
+ }
+ return categoriesList
+ }
+
+ getFreshItems = (onHide) => {
+ const { getExpenseCategories } = this.props
+ getExpenseCategories()
+
+ setTimeout(() => {
+ onHide && onHide()
+ }, 400);
+
+ }
+
+ render() {
+
+ const {
+ navigation,
+ loading,
+ language,
+ categories,
+ } = this.props;
+
+ const {
+ search,
+ categoriesFilter,
+ found,
+ refreshing
+ } = this.state
+
+ let categoriesList = [];
+ categoriesList = this.itemList(categories)
+
+ let empty = (!search) ? {
+ description: Lng.t("categories.empty.description", { locale: language }),
+ buttonTitle: Lng.t("categories.empty.buttonTitle", { locale: language }),
+ buttonPress: () => navigation.navigate(ROUTES.CATEGORY, { type: CATEGORY_ADD }),
+ } : {}
+
+
+ return (
+
+ navigation.navigate(ROUTES.SETTING_LIST),
+ title: Lng.t("header.expenseCategory", { locale: language }),
+ titleStyle: styles.titleStyle,
+ placement: "center",
+ rightIcon: "plus",
+ rightIconPress: () => navigation.navigate(ROUTES.CATEGORY, { type: CATEGORY_ADD }),
+ }}
+ onSearch={this.onSearch}
+ bottomDivider
+ loadingProps={{ is: loading }}
+ >
+
+
+ {
+ this.getFreshItems(onHide)
+ }}
+ onPress={this.onSelectCategory}
+ loading={loading}
+ isEmpty={found ? categoriesList.length <= 0 : true}
+ bottomDivider
+ emptyContentProps={{
+ title: found ?
+ Lng.t("categories.empty.title", { locale: language }) :
+ Lng.t("search.noResult", { locale: language, search }),
+ ...empty
+ }}
+ />
+
+
+
+
+ );
+ }
+}
+
diff --git a/src/features/settings/components/Categories/styles.js b/src/features/settings/components/Categories/styles.js
new file mode 100644
index 00000000..1fd74765
--- /dev/null
+++ b/src/features/settings/components/Categories/styles.js
@@ -0,0 +1,23 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { isIPhoneX, headerTitle } from '../../../../api/helper';
+import { fonts } from '../../../../styles/fonts';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ listViewContainer: {
+ flex: 1,
+ paddingBottom: isIPhoneX() ? 30 : 0,
+ },
+ titleStyle: {
+ color: colors.dark,
+ fontFamily: fonts.poppins,
+ ...headerTitle({ marginLeft: -25, marginRight: -25 })
+ }
+});
diff --git a/src/features/settings/components/Category/index.js b/src/features/settings/components/Category/index.js
new file mode 100644
index 00000000..1c7b2ee6
--- /dev/null
+++ b/src/features/settings/components/Category/index.js
@@ -0,0 +1,236 @@
+// @flow
+
+import React from 'react';
+import { View, Alert } from 'react-native';
+import { Field, change } from 'redux-form';
+import styles from './styles';
+import {
+ InputField,
+ CtButton,
+ DefaultLayout
+} from '../../../../components';
+import { BUTTON_COLOR } from '../../../../api/consts/core';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import Lng from '../../../../api/lang/i18n';
+import { CATEGORY_EDIT, CATEGORY_ADD, CATEGORY_FORM } from '../../constants';
+import { ROUTES } from '../../../../navigation/routes';
+import { MAX_LENGTH, alertMe } from '../../../../api/global';
+
+type IProps = {
+ navigation: Object,
+ handleSubmit: Function,
+ getEditCategory: Function,
+ createCategory: Function,
+ editCategory: Function,
+ language: String,
+ type: String,
+ getEditCategoryLoading: Boolean,
+ categoryLoading: Boolean,
+}
+
+export class Category extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ };
+ }
+
+ componentDidMount() {
+ const {
+ navigation,
+ getEditCategory,
+ type,
+ onFirstTimeCreateExpense,
+ } = this.props;
+
+ if (type === CATEGORY_EDIT) {
+
+ let id = navigation.getParam('categoryId', null)
+ getEditCategory({
+ id,
+ onResult: (val) => {
+
+ const { name, description } = val
+ this.setFormField('name', name)
+ this.setFormField('description', description)
+ }
+ });
+ }
+
+ !onFirstTimeCreateExpense ? goBack(MOUNT, navigation) : goBack(MOUNT, navigation, ROUTES.MAIN_EXPENSES)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(CATEGORY_FORM, field, value));
+ };
+
+ onSubmitCategory = (values) => {
+
+ const {
+ type,
+ createCategory,
+ editCategory,
+ navigation,
+ categoryLoading,
+ onFirstTimeCreateExpense
+ } = this.props
+
+ if (!categoryLoading) {
+ if (type === CATEGORY_ADD)
+ createCategory({
+ params: values,
+ onResult: (res) => {
+ onFirstTimeCreateExpense && onFirstTimeCreateExpense(res)
+
+ navigation.goBack(null)
+ }
+ })
+ else {
+ let id = navigation.getParam('categoryId', null);
+ editCategory({ id, params: values, navigation })
+ }
+ }
+ };
+
+ removeCategory = () => {
+
+ const { removeCategory, navigation, language, formValues: { name } } = this.props
+
+ Alert.alert(
+ Lng.t("alert.title", { locale: language }),
+ Lng.t("categories.alertDescription", { locale: language }),
+ [
+ {
+ text: 'OK',
+ onPress: () => removeCategory({
+ id: navigation.getParam('categoryId', null),
+ navigation,
+ onResult: () => {
+ alertMe({ title: `${name} ${Lng.t("categories.alreadyUsed", { locale: language })}` })
+ }
+ })
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ }
+
+ BOTTOM_ACTION = (handleSubmit) => {
+
+ const {
+ language,
+ categoryLoading,
+ type
+ } = this.props
+
+ return (
+
+
+
+ {type === CATEGORY_EDIT &&
+
+ }
+
+ )
+ }
+
+ render() {
+ const {
+ navigation,
+ handleSubmit,
+ language,
+ getEditCategoryLoading,
+ type,
+ onFirstTimeCreateExpense,
+ } = this.props;
+
+
+ let categoryRefs = {}
+
+ return (
+ {
+ !onFirstTimeCreateExpense ? navigation.goBack(null) :
+ navigation.navigate(ROUTES.MAIN_EXPENSES)
+ },
+ title: type === CATEGORY_EDIT ?
+ Lng.t("header.editCategory", { locale: language }) :
+ Lng.t("header.addCategory", { locale: language }),
+ placement: "center",
+ rightIcon: "save",
+ rightIconProps: {
+ solid: true,
+ },
+ rightIconPress: handleSubmit(this.onSubmitCategory),
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ loadingProps={{
+ is: getEditCategoryLoading
+ }}
+ >
+
+
+ {
+ categoryRefs.description.focus();
+ }
+ }}
+ validationStyle={styles.inputFieldValidation}
+ />
+
+ {
+ categoryRefs.description = ref;
+ }}
+ />
+
+
+
+ );
+ }
+}
+
diff --git a/src/features/settings/components/Category/styles.js b/src/features/settings/components/Category/styles.js
new file mode 100644
index 00000000..91403de0
--- /dev/null
+++ b/src/features/settings/components/Category/styles.js
@@ -0,0 +1,37 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ mainContainer: {
+ paddingHorizontal: 20,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ bodyContainer: {
+ paddingHorizontal: 22,
+ paddingVertical: 17,
+ },
+ submitButton: {
+ paddingHorizontal: 10,
+ },
+ multipleButton: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ flexDirection: 'row'
+ },
+ btnContainerStyle: {
+ marginHorizontal: 10,
+ },
+ flex: {
+ flex: 1
+ },
+ handleBtn: {
+ marginHorizontal: 5,
+ },
+
+});
diff --git a/src/features/settings/components/Company/index.js b/src/features/settings/components/Company/index.js
new file mode 100644
index 00000000..69d259f2
--- /dev/null
+++ b/src/features/settings/components/Company/index.js
@@ -0,0 +1,317 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import styles from './styles';
+import { DefaultLayout, CtButton, InputField, FilePicker, AssetImage } from '../../../../components';
+import { Field, change } from 'redux-form';
+import Lng from '../../../../api/lang/i18n';
+import AddressFieldContainer from '../../../customers/containers/AddressField';
+import { EDIT_COMPANY } from '../../constants';
+import { goBack, UNMOUNT, MOUNT } from '../../../../navigation/actions';
+import { MAX_LENGTH } from '../../../../api/global';
+
+
+type IProps = {
+ navigation: Object,
+ getCompanyInformation: Function,
+ getCountries: Function,
+ getStates: Function,
+ getCities: Function,
+ editCompanyInformation: Function,
+ handleSubmit: Function,
+ language: String,
+ editCompanyLoading: Boolean,
+ getCompanyInfoLoading: Boolean,
+ countriesLoading: Boolean,
+ statesLoading: Boolean,
+ citiesLoading: Boolean
+}
+
+let companyField = [
+ "country_id",
+ "state_id",
+ "city_id",
+ "zip",
+ "address_street_1",
+ "address_street_2",
+ "phone",
+]
+export class Company extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ country: ' ',
+ state: ' ',
+ city: ' ',
+ image: null,
+ logo: null,
+ fileLoading: false,
+ }
+ }
+
+ componentDidMount() {
+ const {
+ getCompanyInformation,
+ getCountries,
+ getStates,
+ getCities,
+ navigation,
+ countries
+ } = this.props
+
+ let hasCountryApiCalled = countries ? (typeof countries === 'undefined' || countries.length === 0) : true
+
+ hasCountryApiCalled && getCountries()
+
+ getCompanyInformation({
+ onResult: (company) => {
+
+ this.setFormField("name", company.company_id ?
+ company.company.name : ''
+ )
+
+ if (company.addresses[0]) {
+
+ companyField.map((field) => {
+ this.setFormField(field, company.addresses[0][field])
+ })
+
+ if (company.addresses[0].country) {
+ let { id, name, code } = company.addresses[0].country
+ this.setState({ country: `${name} (${code})` })
+ getStates({ countryId: id })
+ }
+ if (company.addresses[0].state) {
+ let { id, name } = company.addresses[0].state
+ this.setState({ state: `${name}` })
+ getCities({ stateID: id })
+ }
+ if (company.addresses[0].city) {
+ let { name } = company.addresses[0].city
+ this.setState({ city: `${name}` })
+ }
+ if (company.company.logo) {
+ this.setState({ image: company.company.logo })
+ }
+
+ }
+ }
+ });
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(EDIT_COMPANY, field, value));
+ };
+
+ onCompanyUpdate = (value) => {
+ const { navigation, editCompanyInformation, editCompanyLoading } = this.props
+ const { logo, fileLoading } = this.state
+
+ if (!fileLoading && !editCompanyLoading) {
+ editCompanyInformation({
+ params: value,
+ logo,
+ navigation
+ })
+ }
+
+ }
+
+ BOTTOM_ACTION = (handleSubmit) => {
+ const { language, editCompanyLoading } = this.props
+ const { fileLoading } = this.state
+
+ return (
+
+
+
+ )
+ }
+
+ render() {
+ const {
+ navigation,
+ handleSubmit,
+ language,
+ getCompanyInfoLoading,
+ countriesLoading,
+ statesLoading,
+ citiesLoading
+ } = this.props;
+
+ let { country, state, city, image } = this.state
+
+ let companyRefs = {}
+
+ return (
+ navigation.goBack(null),
+ title: Lng.t("header.setting.company", { locale: language }),
+ titleStyle: styles.titleStyle,
+ placement: "center",
+ rightIcon: "save",
+ rightIconProps: {
+ solid: true,
+ },
+ rightIconPress: handleSubmit(this.onCompanyUpdate),
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ loadingProps={{
+ is: getCompanyInfoLoading || countriesLoading
+ }}
+ >
+
+
+
+ this.setState({ logo: val })
+ }
+ imageUrl={image}
+ containerStyle={{
+ marginTop: 15,
+ }}
+ fileLoading={(val) => {
+ this.setState({ fileLoading: val })
+ }}
+ />
+
+ {
+ companyRefs.phone.focus();
+ }
+ }}
+ />
+
+ {
+ companyRefs.phone = ref;
+ }}
+ />
+
+
+ this.setFormField("country_id", val)
+ }
+ countriesLoading={countriesLoading}
+ />
+
+
+ this.setFormField("state_id", val)
+ }
+ statesLoading={statesLoading}
+ />
+
+ {
+ this.setFormField("city_id", val)
+ companyRefs.street1.focus();
+ }}
+ citiesLoading={citiesLoading}
+ />
+
+ {
+ companyRefs.street1 = ref;
+ }}
+ />
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/settings/components/Company/styles.js b/src/features/settings/components/Company/styles.js
new file mode 100644
index 00000000..77db3792
--- /dev/null
+++ b/src/features/settings/components/Company/styles.js
@@ -0,0 +1,33 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { headerTitle } from '../../../../api/helper';
+import { fonts } from '../../../../styles/fonts';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ mainContainer: {
+ paddingHorizontal: 20,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ submitButton: {
+ paddingHorizontal: 10
+ },
+ addressStreetField: {
+ marginTop: -20,
+ },
+ fakeInputPlaceholderStyle: {
+ paddingLeft: 10,
+ },
+ images: {
+ height: 110,
+ resizeMode: "contain",
+ },
+ titleStyle: {
+ ...headerTitle({ marginLeft: -12, marginRight: -15 })
+ }
+});
diff --git a/src/features/settings/components/LanguageAndCurrency/index.js b/src/features/settings/components/LanguageAndCurrency/index.js
new file mode 100644
index 00000000..bea1f143
--- /dev/null
+++ b/src/features/settings/components/LanguageAndCurrency/index.js
@@ -0,0 +1,273 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import styles from './styles';
+import {
+ DefaultLayout,
+ CtButton,
+ SelectField,
+} from '../../../../components';
+import { Field, change } from 'redux-form';
+import Lng from '../../../../api/lang/i18n';
+import { EDIT_LANGUAGE_AND_CURRENCY } from '../../constants';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { SymbolStyle } from '../../../../components/CurrencyFormat/styles';
+import { headerTitle } from '../../../../api/helper';
+
+type IProps = {
+ navigation: Object,
+ language: String,
+ handleSubmit: Function,
+ handleSubmit: Function,
+ formValues: Object,
+ languages: Object,
+ timezones: Object,
+ dateFormats: Object,
+ currencies: Object,
+ getPreferencesLoading: Boolean,
+ getSettingItemLoading: Boolean
+}
+
+
+export class LanguageAndCurrency extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ currencyList: [],
+ languagesList: [],
+ }
+ }
+
+ componentWillMount() {
+ const {
+ getPreferences,
+ } = this.props
+
+ getPreferences({
+ onResult: (val) => {
+ const { currencies, languages } = val
+ this.setState({
+ currencyList: this.getCurrenciesList(currencies),
+ languagesList: this.getLanguagesList(languages),
+ })
+ }
+ })
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(EDIT_LANGUAGE_AND_CURRENCY, field, value));
+ };
+
+ onSubmit = (values) => {
+ const {
+ navigation,
+ editPreferences,
+ clearPreferences,
+ currencies
+ } = this.props
+
+ clearPreferences()
+ editPreferences({ params: values, navigation, currencies })
+
+ }
+
+ BOTTOM_ACTION = (handleSubmit) => {
+ const { editPreferencesLoading, language } = this.props
+ return (
+
+
+
+ )
+ }
+
+ getCurrenciesList = (currencies) => {
+
+ let currencyList = []
+ if (typeof currencies !== 'undefined' && currencies.length != 0) {
+ currencyList = currencies.map((currency) => {
+
+ const { name, code, symbol } = currency
+ return {
+ title: name,
+ subtitle: {
+ title: code,
+ },
+ rightTitle: symbol || '-',
+ fullItem: currency
+ }
+ })
+ }
+ return currencyList
+ }
+
+ getLanguagesList = (languages) => {
+ let languageList = []
+ if (typeof languages !== 'undefined' && languages) {
+ languageList = languages.map((language) => {
+
+ let { name } = language
+ return {
+ title: name,
+ leftAvatar: name.toUpperCase().charAt(0),
+ fullItem: language
+ }
+ })
+ }
+ return languageList
+ }
+
+ getSelectedField = (items, find, field) => {
+ let newData = []
+ if (typeof items !== 'undefined') {
+
+ newData = items.filter((item) => {
+ let filterData = false
+ let itemField = item.fullItem ?
+ item.fullItem[field].toString() : item[field].toString()
+
+ if (itemField === find)
+ filterData = true
+
+ return filterData
+ });
+
+ }
+
+ if (newData.length !== 0) {
+ let { name } = newData[0].fullItem
+ return name
+ }
+ return ' '
+ }
+
+ render() {
+
+ const {
+ navigation,
+ handleSubmit,
+ language,
+ formValues: {
+ currency,
+ },
+ formValues,
+ isLoading
+ } = this.props;
+
+ const { currencyList, languagesList } = this.state
+
+ return (
+ navigation.goBack(null),
+ title: Lng.t("header.setting.LanguageAndCurrency", { locale: language }),
+ placement: "center",
+ rightIcon: "save",
+ rightIconProps: {
+ solid: true,
+ },
+ rightIconPress: handleSubmit(this.onSubmit),
+ titleStyle: styles.titleStyle
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ loadingProps={{
+ is: isLoading || currencyList.length === 0 || languagesList.length === 0
+ }}
+ >
+
+
+
+ {
+ this.setFormField('language', val.code)
+ }}
+ headerProps={{
+ title: Lng.t("languages.title", { locale: language }),
+ rightIconPress: null
+ }}
+ listViewProps={{
+ hasAvatar: true,
+ }}
+ emptyContentProps={{
+ contentType: "languages",
+ }}
+ isRequired
+ />
+
+ {
+ this.setFormField('currency', val.id)
+ }}
+ headerProps={{
+ title: Lng.t("currencies.title", { locale: language }),
+ titleStyle: headerTitle({ marginLeft: -20, marginRight: -52 }),
+ rightIconPress: null
+ }}
+ emptyContentProps={{
+ contentType: "currencies",
+ }}
+ isRequired
+ listViewProps={{
+ contentContainerStyle: { flex: 5 },
+ rightTitleStyle: SymbolStyle
+ }}
+ />
+
+
+
+ );
+ }
+}
diff --git a/src/features/settings/components/LanguageAndCurrency/styles.js b/src/features/settings/components/LanguageAndCurrency/styles.js
new file mode 100644
index 00000000..9112719b
--- /dev/null
+++ b/src/features/settings/components/LanguageAndCurrency/styles.js
@@ -0,0 +1,30 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { isIPhoneX } from '../../../../api/helper';
+import { fonts } from '../../../../styles/fonts';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ mainContainer: {
+ paddingHorizontal: 20,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ submitButton: {
+ paddingHorizontal: 10
+ },
+ selectedField: {
+ paddingLeft: 47,
+ },
+ titleStyle: {
+ color: colors.dark,
+ fontFamily: fonts.poppins,
+ marginLeft: isIPhoneX() ? 0 : -7,
+ marginRight: isIPhoneX() ? 0 : -7,
+ textAlign: "center",
+ },
+});
diff --git a/src/features/settings/components/Notification/index.js b/src/features/settings/components/Notification/index.js
new file mode 100644
index 00000000..633762a9
--- /dev/null
+++ b/src/features/settings/components/Notification/index.js
@@ -0,0 +1,196 @@
+// @flow
+
+import React from 'react';
+import { View, Text, TouchableOpacity } from 'react-native';
+import styles from './styles';
+import {
+ DefaultLayout,
+ CtButton,
+ InputField,
+ ToggleSwitch,
+ CtDivider
+} from '../../../../components';
+import { Field, change } from 'redux-form';
+import Lng from '../../../../api/lang/i18n';
+import { NOTIFICATION } from '../../constants';
+import { colors } from '../../../../styles/colors';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+
+
+type IProps = {
+ navigation: Object,
+ handleSubmit: Function,
+ language: String,
+ getAccountLoading: Boolean,
+}
+export class Notification extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ invoiceStatus: null,
+ estimateStatus: null,
+ email: '',
+ visibleToast: ''
+ }
+ }
+
+ componentWillMount() {
+ const { getSettingItem } = this.props
+
+ getSettingItem({
+ key: 'notify_invoice_viewed',
+ onResult: (val) => {
+ this.setState({ invoiceStatus: val !== null ? val : 'NO' })
+ }
+ })
+
+ getSettingItem({
+ key: 'notify_estimate_viewed',
+ onResult: (val) => {
+ this.setState({ estimateStatus: val !== null ? val : 'NO' })
+ }
+ })
+
+ getSettingItem({
+ key: 'notification_email',
+ onResult: (val) => {
+ this.setFormField('notification_email', val)
+ }
+ })
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(NOTIFICATION, field, value));
+ };
+
+ onNotificationSubmit = ({ notification_email }) => {
+ const { editSettingItem, navigation } = this.props
+
+ editSettingItem({
+ params: {
+ key: 'notification_email',
+ value: notification_email
+ },
+ navigation
+ })
+
+ }
+
+ invoiceStatus = (status) => {
+ const { editSettingItem } = this.props
+
+ editSettingItem({
+ params: {
+ key: 'notify_invoice_viewed',
+ value: status === true ? 'YES' : 'NO'
+ },
+ onResult: () => { this.toggleToast('settings.notifications.invoiceViewedUpdated') }
+ })
+ }
+
+ estimateStatus = (status) => {
+ const { editSettingItem } = this.props
+
+ editSettingItem({
+ params: {
+ key: 'notify_estimate_viewed',
+ value: status === true ? 'YES' : 'NO'
+ },
+ onResult: () => { this.toggleToast('settings.notifications.estimateViewedUpdated') }
+ })
+ }
+
+ toggleToast = (msg) => {
+ this.setState({ visibleToast: msg })
+ }
+
+ render() {
+ const {
+ navigation,
+ handleSubmit,
+ language,
+ getSettingItemLoading,
+ } = this.props;
+
+ const { invoiceStatus, estimateStatus, visibleToast } = this.state
+ return (
+ navigation.goBack(null),
+ title: Lng.t("header.notifications", { locale: language }),
+ placement: "center",
+ rightIcon: "save",
+ rightIconProps: {
+ solid: true,
+ },
+ leftIconStyle: { color: colors.dark2 },
+ rightIconPress: handleSubmit(this.onNotificationSubmit),
+ }}
+ loadingProps={{
+ is: getSettingItemLoading || invoiceStatus === null ||
+ estimateStatus === null
+ }}
+ toastProps={{
+ message: Lng.t(visibleToast, { locale: language }),
+ visible: visibleToast,
+ containerStyle: styles.toastContainer
+ }}
+ >
+
+
+
+
+
+
+
+ this.invoiceStatus(val)
+ }
+ />
+
+
+ this.estimateStatus(val)
+ }
+ mainContainerStyle={{ marginTop: 12 }}
+ />
+
+
+
+ );
+ }
+}
diff --git a/src/features/settings/components/Notification/styles.js b/src/features/settings/components/Notification/styles.js
new file mode 100644
index 00000000..21971af5
--- /dev/null
+++ b/src/features/settings/components/Notification/styles.js
@@ -0,0 +1,31 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { isIPhoneX } from '../../../../api/helper';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ mainContainer: {
+ paddingHorizontal: 20,
+ marginTop: 15,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ submitButton: {
+ paddingHorizontal: 10
+ },
+ dividerLine: {
+ marginTop: 18,
+ marginBottom: 18,
+ backgroundColor: colors.gray,
+ borderColor: colors.gray,
+ borderWidth: 0.2,
+ },
+ toastContainer: {
+ bottom: isIPhoneX() ? 40 : 20,
+ paddingHorizontal: 13,
+ }
+});
diff --git a/src/features/settings/components/Preferences/index.js b/src/features/settings/components/Preferences/index.js
new file mode 100644
index 00000000..ef9ab031
--- /dev/null
+++ b/src/features/settings/components/Preferences/index.js
@@ -0,0 +1,364 @@
+// @flow
+
+import React from 'react';
+import { View, Text } from 'react-native';
+import styles from './styles';
+import {
+ DefaultLayout,
+ CtButton,
+ SelectField,
+ ToggleSwitch,
+ CtDivider
+} from '../../../../components';
+import { Field, change } from 'redux-form';
+import Lng from '../../../../api/lang/i18n';
+import { EDIT_PREFERENCES } from '../../constants';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { headerTitle } from '../../../../api/helper';
+
+type IProps = {
+ navigation: Object,
+ language: String,
+ handleSubmit: Function,
+ handleSubmit: Function,
+ formValues: Object,
+ languages: Object,
+ timezones: Object,
+ dateFormats: Object,
+ currencies: Object,
+}
+
+export class Preferences extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ timezoneList: [],
+ dateFormatList: [],
+ fiscalYearLst: [],
+ discountPerItem: null,
+ taxPerItem: null,
+ visibleToast: false
+ }
+ }
+
+ componentWillMount() {
+ const {
+ getPreferences,
+ getSettingItem
+ } = this.props
+
+ getPreferences({
+ onResult: (val) => {
+ const { time_zones, date_formats, fiscal_years } = val
+ const dateFormatList = this.getDateFormatList(date_formats)
+ const timezoneList = this.getTimeZoneList(time_zones)
+ const fiscalYearLst = this.getFiscalYearList(fiscal_years)
+ this.setState({ timezoneList, dateFormatList, fiscalYearLst })
+ }
+ })
+ getSettingItem({
+ key: 'discount_per_item',
+ onResult: (val) => {
+ this.setState({ discountPerItem: val !== null ? val : 'NO' })
+ }
+ })
+
+ getSettingItem({
+ key: 'tax_per_item',
+ onResult: (val) => {
+ this.setState({ taxPerItem: val !== null ? val : 'NO' })
+ }
+ })
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ setFormField = (field, value) => {
+ this.props.dispatch(change(EDIT_PREFERENCES, field, value));
+ };
+
+ onSubmitPreferences = (values) => {
+ const {
+ navigation,
+ editPreferences,
+ clearPreferences,
+ currencies,
+ editPreferencesLoading, editSettingItemLoading
+ } = this.props
+
+ if (!(editPreferencesLoading || editSettingItemLoading)) {
+ clearPreferences()
+ editPreferences({ params: values, navigation, currencies })
+ }
+
+ }
+
+ BOTTOM_ACTION = (handleSubmit) => {
+ const { editPreferencesLoading, language } = this.props
+ return (
+
+
+
+ )
+ }
+
+ getTimeZoneList = (timezones) => {
+
+ let timeZoneList = []
+ if (typeof timezones !== 'undefined') {
+
+ timeZoneList = timezones.map((timezone) => {
+
+ return {
+ title: timezone.key,
+ fullItem: timezone
+ }
+ })
+ }
+
+ return timeZoneList
+
+ }
+
+ getFiscalYearList = (fiscalYears) => {
+
+ let years = []
+ if (typeof fiscalYears !== 'undefined') {
+ years = fiscalYears.map((year) => {
+
+ const { key } = year
+ return {
+ title: key,
+ fullItem: year
+ }
+ })
+ }
+ return years
+ }
+
+ getDateFormatList = (dateFormats) => {
+
+ let dateFormatList = []
+ if (typeof dateFormats !== 'undefined') {
+ dateFormatList = dateFormats.map((dateformat) => {
+
+ const { display_date } = dateformat
+ return {
+ title: display_date,
+ fullItem: dateformat
+ }
+ })
+ }
+ return dateFormatList
+ }
+
+ setDiscountPerItem = (val) => {
+ const { editSettingItem } = this.props
+
+ editSettingItem({
+ params: {
+ key: 'discount_per_item',
+ value: val === true ? 'YES' : 'NO'
+ },
+ onResult: () => { this.toggleToast() }
+ })
+ }
+
+ setTaxPerItem = (val) => {
+ const { editSettingItem } = this.props
+
+ editSettingItem({
+ params: {
+ key: 'tax_per_item',
+ value: val === true ? 'YES' : 'NO'
+ },
+ onResult: () => { this.toggleToast() }
+ })
+ }
+
+ toggleToast = () => {
+ this.setState({ visibleToast: true })
+ }
+
+ render() {
+
+ const {
+ navigation,
+ handleSubmit,
+ language,
+ formValues: {
+ time_zone,
+ },
+ dateFormats,
+ isLoading,
+ } = this.props;
+
+ const {
+ timezoneList,
+ dateFormatList,
+ fiscalYearLst,
+ discountPerItem,
+ taxPerItem,
+ visibleToast
+ } = this.state
+
+ return (
+ navigation.goBack(null),
+ title: Lng.t("header.setting.preferences", { locale: language }),
+ placement: "center",
+ rightIcon: "save",
+ rightIconProps: {
+ solid: true,
+ },
+ rightIconPress: handleSubmit(this.onSubmitPreferences),
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ loadingProps={{
+ is: isLoading || timezoneList.length === 0 || dateFormatList.length === 0 || discountPerItem === null || taxPerItem === null
+ }}
+ toastProps={{
+ message: Lng.t("settings.preferences.settingUpdate", { locale: language }),
+ visible: visibleToast
+ }}
+ >
+
+
+
+ {
+ this.setFormField('time_zone', val.value)
+ }}
+ headerProps={{
+ title: Lng.t("timeZones.title", { locale: language }),
+ titleStyle: headerTitle({ marginLeft: -23, marginRight: -40 }),
+ rightIconPress: null
+ }}
+ emptyContentProps={{
+ contentType: "timeZones",
+ }}
+ isRequired
+ />
+
+ {
+ this.setFormField('carbon_date_format', val.carbon_format_value)
+ this.setFormField('moment_date_format', val.moment_format_value)
+ this.setFormField('date_format', val.carbon_format_value)
+ }}
+ headerProps={{
+ title: Lng.t("dateFormats.title", { locale: language }),
+ titleStyle: headerTitle({ marginLeft: -20, marginRight: -55 }),
+ rightIconPress: null
+ }}
+ emptyContentProps={{
+ contentType: "dateFormats",
+ }}
+ isRequired
+ />
+
+ {
+ this.setFormField('fiscal_year', val.value)
+ }}
+ headerProps={{
+ title: Lng.t("fiscalYears.title", { locale: language }),
+ titleStyle: headerTitle({ marginLeft: -15, marginRight: -35 }),
+ rightIconPress: null
+ }}
+ emptyContentProps={{
+ contentType: "fiscalYears",
+ }}
+ isRequired
+ />
+
+
+ this.setDiscountPerItem(val)
+ }
+ />
+
+ this.setTaxPerItem(val)
+ }
+ mainContainerStyle={{ marginVertical: 12 }}
+ />
+
+
+
+ );
+ }
+}
diff --git a/src/features/settings/components/Preferences/styles.js b/src/features/settings/components/Preferences/styles.js
new file mode 100644
index 00000000..a9c4176e
--- /dev/null
+++ b/src/features/settings/components/Preferences/styles.js
@@ -0,0 +1,35 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ mainContainer: {
+ paddingHorizontal: 20,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ submitButton: {
+ paddingHorizontal: 10
+ },
+ selectedField: {
+ paddingLeft: 47,
+ },
+ dividerLine: {
+ marginTop: 18,
+ marginBottom: 18,
+ backgroundColor: colors.gray,
+ borderColor: colors.lightGray,
+ borderWidth: 0.2,
+ },
+ label: {
+ color: colors.dark3,
+ fontFamily: fonts.poppinsMedium,
+ fontSize: 20,
+ marginBottom: 10
+ },
+});
diff --git a/src/features/settings/components/Settings/index.js b/src/features/settings/components/Settings/index.js
new file mode 100644
index 00000000..1f3da4c4
--- /dev/null
+++ b/src/features/settings/components/Settings/index.js
@@ -0,0 +1,84 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import styles from './styles';
+import { ListView, DefaultLayout } from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import { colors } from '../../../../styles/colors';
+import Lng from '../../../../api/lang/i18n';
+import { SETTINGS_MENU } from '../../constants';
+import { MOUNT, goBack, UNMOUNT } from '../../../../navigation/actions';
+
+export class Settings extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ endpointVisible: false
+ }
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation, ROUTES.MAIN_MORE)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ onSelectMenu = (item) => {
+ const { navigation } = this.props
+
+ if (item.route) {
+ (item.route === ROUTES.ENDPOINTS_SETTINGS)
+ ? navigation.navigate(item.route, { skipEndpoint: true }) :
+ navigation.navigate(item.route)
+ } else {
+ this[item.action]()
+ }
+ }
+
+ onLogout = () => {
+ const { navigation, logout } = this.props
+ logout({ navigation })
+ }
+
+ render() {
+ const { navigation, language } = this.props;
+
+ const { endpointVisible } = this.state;
+
+ return (
+
+ navigation.navigate(ROUTES.MAIN_MORE),
+ title: Lng.t("header.settings", { locale: language }),
+ leftIconStyle: { color: colors.dark2 }
+ }}
+ hasSearchField={false}
+ >
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/settings/components/Settings/styles.js b/src/features/settings/components/Settings/styles.js
new file mode 100644
index 00000000..5acfd788
--- /dev/null
+++ b/src/features/settings/components/Settings/styles.js
@@ -0,0 +1,31 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { fonts } from '../../../../styles/fonts';
+import { isIPhoneX } from '../../../../api/helper';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ listViewTitle: {
+ fontFamily: fonts.poppins,
+ color: colors.secondary,
+ marginLeft: isIPhoneX() ? 0 : -4,
+ marginRight: isIPhoneX() ? 0 : -11,
+ },
+ listViewContainer: {
+ marginTop: 10,
+ marginHorizontal: isIPhoneX() ? 0 : -5,
+ },
+ listViewIcon: {
+ width: 25,
+ textAlign: "center",
+ },
+ itemContainer: {
+ marginVertical: -2,
+ }
+});
diff --git a/src/features/settings/components/Tax/index.js b/src/features/settings/components/Tax/index.js
new file mode 100644
index 00000000..f2f4e843
--- /dev/null
+++ b/src/features/settings/components/Tax/index.js
@@ -0,0 +1,199 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import styles from './styles';
+import { DefaultLayout, CtButton, InputField, ToggleSwitch } from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import { BUTTON_COLOR } from '../../../../api/consts/core';
+import { Field } from 'redux-form';
+import Lng from '../../../../api/lang/i18n';
+import { ADD_TAX } from '../../constants';
+import { Alert } from 'react-native';
+import { UNMOUNT, MOUNT, goBack } from '../../../../navigation/actions';
+import { MAX_LENGTH, alertMe } from '../../../../api/global';
+
+export class Tax extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ endpointVisible: false
+ }
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+
+ onSave = (tax) => {
+ const { addTax, navigation, type, editTax, loading } = this.props
+ const isCreate = (type === ADD_TAX)
+
+ if (!loading) {
+ isCreate ?
+ addTax({
+ tax,
+ onResult: (res) => {
+ const onSelect = navigation.getParam('onSelect', null)
+ onSelect && onSelect([{ ...res, tax_type_id: res.id }])
+ navigation.goBack(null)
+ }
+ }) : editTax({
+ tax,
+ onResult: () => {
+ navigation.goBack(null)
+ }
+ })
+ }
+ }
+
+ removeTax = () => {
+ const {
+ removeTax,
+ taxId,
+ navigation,
+ language,
+ initialValues: { name }
+ } = this.props
+
+ Alert.alert(
+ Lng.t("alert.title", { locale: language }),
+ Lng.t("taxes.alertDescription", { locale: language }),
+ [
+ {
+ text: 'OK',
+ onPress: () => removeTax({
+ id: taxId,
+ onResult: (val) => {
+ val ? navigation.navigate(ROUTES.TAXES) :
+ alertMe({ title: `${name} ${Lng.t("taxes.alreadyUsed", { locale: language })}` })
+ }
+ })
+ },
+ {
+ text: 'Cancel',
+ onPress: () => { },
+ style: 'cancel',
+ },
+ ],
+ { cancelable: false }
+ );
+ }
+
+ BOTTOM_ACTION = (handleSubmit) => {
+ const { loading, language, type } = this.props
+ const isCreate = (type === ADD_TAX)
+
+ return (
+
+
+ {!isCreate &&
+
+ }
+
+ )
+ }
+
+ render() {
+ const { navigation, handleSubmit, language, type, initialValues } = this.props;
+ const isCreate = (type === ADD_TAX)
+
+ let taxRefs = {}
+
+ return (
+ navigation.goBack(null),
+ title: isCreate ? Lng.t("header.addTaxes", { locale: language }) : Lng.t("header.editTaxes", { locale: language }),
+ placement: "center",
+ rightIcon: "save",
+ rightIconProps: {
+ solid: true,
+ },
+ rightIconPress: handleSubmit(this.onSave),
+ }}
+ bottomAction={this.BOTTOM_ACTION(handleSubmit)}
+ >
+
+ {
+ taxRefs.percent.focus();
+ }
+ }}
+ />
+
+ {
+ taxRefs.description.focus();
+ }
+ }}
+ refLinkFn={(ref) => {
+ taxRefs.percent = ref;
+ }}
+ maxNumber={100}
+ />
+
+ {
+ taxRefs.description = ref;
+ }}
+ height={80}
+ />
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/features/settings/components/Tax/styles.js b/src/features/settings/components/Tax/styles.js
new file mode 100644
index 00000000..8413d077
--- /dev/null
+++ b/src/features/settings/components/Tax/styles.js
@@ -0,0 +1,29 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ mainContainer: {
+ paddingHorizontal: 20,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ submitButton: {
+ paddingHorizontal: 10,
+ },
+ multipleButton: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ flexDirection: 'row'
+ },
+ handleBtn: {
+ marginHorizontal: 5,
+ },
+ buttonContainer: {
+ flex: 1
+ }
+});
diff --git a/src/features/settings/components/Taxes/index.js b/src/features/settings/components/Taxes/index.js
new file mode 100644
index 00000000..b1d8772b
--- /dev/null
+++ b/src/features/settings/components/Taxes/index.js
@@ -0,0 +1,155 @@
+// @flow
+
+import React from 'react';
+import { View } from 'react-native';
+import styles from './styles';
+import { ListView, MainLayout } from '../../../../components';
+import { ROUTES } from '../../../../navigation/routes';
+import Lng from '../../../../api/lang/i18n';
+import { EDIT_TAX, ADD_TAX } from '../../constants';
+import { goBack, MOUNT, UNMOUNT } from '../../../../navigation/actions';
+import { itemsDescriptionStyle } from '../../../invoices/components/Invoice/styles';
+
+export class Taxes extends React.Component {
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ refreshing: false,
+ search: '',
+ found: true,
+ taxesFilter: []
+ };
+ }
+
+ componentDidMount() {
+ const { navigation } = this.props
+ goBack(MOUNT, navigation)
+ }
+
+ componentWillUnmount() {
+ goBack(UNMOUNT)
+ }
+
+ onSearch = (search) => {
+
+ const { taxTypes } = this.props;
+ let searchFields = [
+ 'name',
+ 'percent'
+ ];
+
+ if (typeof taxTypes !== 'undefined' && taxTypes.length != 0) {
+
+ let newData = taxTypes.filter(({ fullItem }) => {
+ let filterData = false
+
+ searchFields.filter((field) => {
+ let itemField = fullItem[field] ? fullItem[field] : ''
+
+ if (typeof itemField === 'number') {
+ itemField = itemField.toString()
+ }
+
+ if (itemField !== null && itemField !== 'undefined') {
+ itemField = itemField.toLowerCase()
+
+ let searchData = search.toString().toLowerCase()
+
+ if (itemField.indexOf(searchData) > -1) {
+ filterData = true
+ }
+ }
+ })
+ return filterData
+ });
+
+ this.setState({
+ taxesFilter: newData,
+ found: newData.length != 0 ? true : false,
+ search
+ })
+ }
+ }
+
+
+ onTaxSelect = (tax) => {
+ const { navigation } = this.props
+ navigation.navigate(ROUTES.TAX, { tax, type: EDIT_TAX })
+ }
+
+ render() {
+ const {
+ taxTypes,
+ navigation,
+ loading,
+ language,
+ getTaxes
+ } = this.props
+
+ const {
+ refreshing,
+ search,
+ found,
+ taxesFilter,
+ } = this.state
+
+ let emptyTitle = Lng.t("taxes.empty.title", { locale: language })
+
+ let empty = (!search) ? {
+ description: Lng.t("taxes.empty.description", { locale: language }),
+ buttonTitle: Lng.t("taxes.empty.buttonTitle", { locale: language }),
+ buttonPress: () => {
+ navigation.navigate(ROUTES.TAX, { type: ADD_TAX })
+ },
+ } : {}
+
+ return (
+
+
+ navigation.navigate(ROUTES.SETTING_LIST),
+ title: Lng.t("header.taxes", { locale: language }),
+ titleStyle: styles.headerTitle,
+ placement: "center",
+ rightIcon: "plus",
+ rightIconPress: () => navigation.navigate(ROUTES.TAX, { type: ADD_TAX }),
+ }}
+ onSearch={this.onSearch}
+ bottomDivider
+ >
+
+
+ {
+ onHide && onHide()
+ getTaxes();
+ }}
+ onPress={this.onTaxSelect}
+ loading={loading}
+ isEmpty={found ? taxTypes.length <= 0 : true}
+ bottomDivider
+ contentContainerStyle={{ flex: 3 }}
+ leftSubTitleStyle={itemsDescriptionStyle(45)}
+ emptyContentProps={{
+ title: found ? emptyTitle :
+ search ?
+ Lng.t("search.noResult", { locale: language, search })
+ : emptyTitle,
+ ...empty
+ }}
+ />
+
+
+
+
+ );
+ }
+}
+
diff --git a/src/features/settings/components/Taxes/styles.js b/src/features/settings/components/Taxes/styles.js
new file mode 100644
index 00000000..2da3ecac
--- /dev/null
+++ b/src/features/settings/components/Taxes/styles.js
@@ -0,0 +1,23 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../../styles/colors';
+import { isIPhoneX } from '../../../../api/helper';
+
+export default styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.veryLightGray,
+ },
+ mainContainer: {
+ paddingHorizontal: 20,
+ },
+ headerContainer: {
+ backgroundColor: colors.veryLightGray,
+ },
+ submitButton: {
+ paddingHorizontal: 10
+ },
+ listViewContainer: {
+ flex: 1,
+ paddingBottom: isIPhoneX() ? 30 : 0,
+ }
+});
diff --git a/src/features/settings/constants.js b/src/features/settings/constants.js
new file mode 100644
index 00000000..9d1f1253
--- /dev/null
+++ b/src/features/settings/constants.js
@@ -0,0 +1,173 @@
+import queryString from 'query-string';
+import { ROUTES } from "../../navigation/routes";
+
+// Forms
+// -----------------------------------------
+export const SET_ENDPOINT_API = 'settingsForm/SET_ENDPOINT_API';
+export const EDIT_ACCOUNT = 'accountForm/EDIT_ACCOUNT';
+export const EDIT_COMPANY = 'accountForm/EDIT_COMPANY';
+export const EDIT_LANGUAGE_AND_CURRENCY = 'accountForm/EDIT_LANGUAGE_AND_CURRENCY';
+export const NOTIFICATION = 'notification/NOTIFICATION';
+
+export const SEARCH_TAX = 'taxForm/SEARCH_TAX';
+export const TAX_FORM = 'taxForm/TAX_FORM';
+
+
+export const CATEGORY_SEARCH = 'categories/CATEGORY_SEARCH';
+export const CATEGORY_FORM = 'categories/CATEGORY_FORM';
+
+// Type
+// -----------------------------------------
+export const CATEGORY_ADD = 'category/CATEGORY_ADD';
+export const CATEGORY_EDIT = 'category/CATEGORY_EDIT';
+export const EDIT_TAX = 'taxType/EDIT_TAX';
+export const ADD_TAX = 'taxType/ADD_TAX';
+
+// Actions
+// -----------------------------------------
+export const SETTINGS_SEARCH = 'settings/SETTINGS_SEARCH';
+export const SETTINGS_TRIGGER_SPINNER = 'settings/SETTINGS_TRIGGER_SPINNER';
+export const LOGOUT = 'settings/LOGOUT';
+
+// Preferences
+export const GET_PREFERENCES = 'preferences/GET_PREFERENCES';
+export const EDIT_PREFERENCES = 'preferences/EDIT_PREFERENCES';
+export const SET_PREFERENCES = 'preferences/SET_PREFERENCES';
+export const CLEAR_PREFERENCES = 'preferences/CLEAR_PREFERENCES';
+
+// Setting
+export const GET_SETTING_ITEM = 'settings/GET_SETTING_ITEM';
+export const EDIT_SETTING_ITEM = 'settings/EDIT_SETTING_ITEM';
+
+// Company
+export const GET_COMPANY_INFO = 'accountForm/GET_COMPANY_INFO';
+export const SET_COMPANY_INFO = 'accountForm/SET_COMPANY_INFO';
+export const EDIT_COMPANY_INFO = 'accountForm/EDIT_COMPANY_INFO';
+
+// Account
+export const GET_ACCOUNT_INFO = 'accountForm/GET_ACCOUNT_INFO';
+export const SET_ACCOUNT_INFO = 'accountForm/SET_ACCOUNT_INFO';
+export const EDIT_ACCOUNT_INFO = 'accountForm/EDIT_ACCOUNT_INFO';
+
+// Categories
+export const GET_EXPENSE_CATEGORIES = 'categories/GET_EXPENSE_CATEGORIES';
+export const GET_CREATE_EXPENSE_CATEGORY = 'categories/GET_CREATE_EXPENSE_CATEGORY';
+
+// Taxes
+export const GET_TAXES = 'taxes/GET_TAXES';
+export const SET_TAXES = 'taxes/SET_TAXES';
+export const SET_TAX = 'taxes/SET_TAX';
+export const SET_EDIT_TAX = 'taxes/SET_EDIT_TAX';
+export const SET_REMOVE_TAX = 'taxes/SET_REMOVE_TAX';
+export const TAX_EDIT = 'taxes/TAX_EDIT';
+export const TAX_ADD = 'taxes/TAX_ADD';
+export const REMOVE_TAX = 'taxes/REMOVE_TAX';
+
+export const CREATE_EXPENSE_CATEGORY = 'categories/CREATE_EXPENSE_CATEGORY';
+export const EDIT_EXPENSE_CATEGORY = 'categories/EDIT_EXPENSE_CATEGORY';
+export const REMOVE_EXPENSE_CATEGORY = 'categories/REMOVE_EXPENSE_CATEGORY';
+
+export const SET_EXPENSE_CATEGORIES = 'categories/SET_EXPENSE_CATEGORIES';
+export const SET_CREATE_EXPENSE_CATEGORIES = 'categories/SET_CREATE_EXPENSE_CATEGORIES';
+export const SET_EDI_EXPENSE_CATEGORIES = 'categories/SET_EDI_EXPENSE_CATEGORIES';
+export const SET_REMOVE_EXPENSE_CATEGORIES = 'categories/SET_REMOVE_EXPENSE_CATEGORIES';
+
+// Menus
+// -----------------------------------------
+export const SETTINGS_MENU = (language, Lng) => {
+ return [
+ {
+ title: Lng.t("settings.accountSettings", { locale: language }),
+ leftIcon: 'user',
+ iconSize: 24,
+ fullItem: {
+ route: ROUTES.ACCOUNT_INFO
+ }
+ },
+ {
+ title: Lng.t("settings.companyInformation", { locale: language }),
+ leftIcon: 'building',
+ iconSize: 24,
+ fullItem: {
+ route: ROUTES.COMPANY_INFO
+ }
+ },
+ {
+ title: Lng.t("settings.preference", { locale: language }),
+ leftIcon: 'sun',
+ leftIconSolid: true,
+ fullItem: {
+ route: ROUTES.PREFERENCES
+ }
+ },
+ {
+ title: Lng.t("settings.notification", { locale: language }),
+ leftIcon: 'bell',
+ iconSize: 25,
+ fullItem: {
+ route: ROUTES.NOTIFICATIONS
+ }
+ },
+ {
+ title: Lng.t("settings.LanguageAndCurrency", { locale: language }),
+ leftIcon: 'language',
+ iconSize: 21,
+ fullItem: {
+ route: ROUTES.LANGUAGE_AND_CURRENCY
+ }
+ },
+ {
+ title: Lng.t("settings.taxes", { locale: language }),
+ leftIcon: 'percent',
+ fullItem: {
+ route: ROUTES.TAXES
+ }
+ },
+ {
+ title: Lng.t("settings.expenseCategory", { locale: language }),
+ leftIcon: 'clipboard-list',
+ iconSize: 24,
+ fullItem: {
+ route: ROUTES.CATEGORIES
+ }
+ },
+ {
+ title: Lng.t("settings.endpoint", { locale: language }),
+ leftIcon: 'link',
+ fullItem: {
+ route: ROUTES.ENDPOINTS_SETTINGS
+ }
+ },
+ ]
+}
+
+// Endpoint Api URL
+// -----------------------------------------
+
+
+export const GET_COMPANY_URL = () => `settings/company`
+export const EDIT_COMPANY_URL = () => `settings/company`
+
+export const GET_ACCOUNT_URL = () => `settings/profile`
+export const EDIT_ACCOUNT_URL = () => `settings/profile`
+
+export const GET_PREFERENCES_URL = () => `settings/general`
+export const EDIT_PREFERENCES_URL = () => `settings/general`
+
+export const GET_GENERAL_SETTING_URL = (key) => `settings/get-setting?key=${key}`
+export const EDIT_GENERAL_SETTING_URL = () => `settings/update-setting`
+
+// Expense Categories
+export const GET_EXPENSE_CATEGORIES_URL = () => `categories`
+export const GET_EDIT_EXPENSE_CATEGORIES_URL = (id) => `categories/${id}/edit`
+
+export const CREATE_EXPENSE_CATEGORIES_URL = () => `categories`
+export const EDIT_EXPENSE_CATEGORIES_URL = (id) => `categories/${id}`
+export const REMOVE_EXPENSE_CATEGORIES_URL = (id) => `categories/${id}`
+
+// Tax Types
+export const GET_SALES_TAXES_URL = () => `tax-types`
+
+export const CREATE_SALES_TAX_URL = () => `tax-types`
+export const EDIT_SALES_TAX_URL = (tax) => `tax-types/${tax.id}`
+export const REMOVE_SALES_TAX_URL = (id) => `tax-types/${id}`
diff --git a/src/features/settings/containers/Account/index.js b/src/features/settings/containers/Account/index.js
new file mode 100644
index 00000000..9d728f41
--- /dev/null
+++ b/src/features/settings/containers/Account/index.js
@@ -0,0 +1,60 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Account } from '../../components/Account';
+import { reduxForm } from 'redux-form';
+import { EDIT_ACCOUNT } from '../../constants';
+import * as AccountAction from '../../actions';
+import { validate } from './validation';
+
+const mapStateToProps = (state) => {
+ const {
+ settings: {
+ loading: {
+ getAccountInfoLoading,
+ editAccountInfoLoading
+ },
+ account
+ },
+ global: { language }
+ } = state
+
+ let isLoading = getAccountInfoLoading || !account
+
+
+ return {
+ isLoading,
+ editAccountLoading: editAccountInfoLoading,
+ language,
+
+ initialValues: !isLoading ? {
+ name: account.name,
+ email: account.email
+ } : null
+
+ };
+};
+
+
+const mapDispatchToProps = {
+ editAccount: AccountAction.editAccount,
+ getAccount: AccountAction.getAccountInformation,
+ editAccount: AccountAction.editAccountInformation
+};
+
+// Redux Forms
+const AccountReduxForm = reduxForm({
+ form: EDIT_ACCOUNT,
+ validate,
+})(Account);
+
+// connect
+const AccountContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(AccountReduxForm);
+
+AccountContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default AccountContainer;
diff --git a/src/features/settings/containers/Account/validation.js b/src/features/settings/containers/Account/validation.js
new file mode 100644
index 00000000..a3f29579
--- /dev/null
+++ b/src/features/settings/containers/Account/validation.js
@@ -0,0 +1,22 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const {
+ name,
+ email,
+ password,
+ confirmPassword
+ } = values;
+
+ errors.name = getError(name, ['requiredField']);
+ errors.email = getError(email, ['requiredField', 'emailFormat']);
+
+ errors.password = getError(password, ['passwordCompared'], { fieldName: confirmPassword });
+ errors.confirmPassword = getError(confirmPassword, ['passwordCompared'], { fieldName: password });
+
+ return errors;
+};
diff --git a/src/features/settings/containers/Categories/index.js b/src/features/settings/containers/Categories/index.js
new file mode 100644
index 00000000..4998dc51
--- /dev/null
+++ b/src/features/settings/containers/Categories/index.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import * as CategoriesAction from '../../actions';
+import { reduxForm } from 'redux-form';
+import { Categories } from '../../components/Categories';
+import { CATEGORY_SEARCH } from '../../constants';
+
+const mapStateToProps = ({ global, settings }) => ({
+ language: global.language,
+ loading: settings.loading.expensesCategoryLoading,
+ categories: settings.categories
+});
+
+const mapDispatchToProps = {
+ getExpenseCategories: CategoriesAction.getExpenseCategories,
+};
+
+// Redux Forms
+const categoriesSearchReduxForm = reduxForm({
+ form: CATEGORY_SEARCH,
+})(Categories);
+
+// connect
+const CategoriesContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(categoriesSearchReduxForm);
+
+CategoriesContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default CategoriesContainer;
diff --git a/src/features/settings/containers/Category/index.js b/src/features/settings/containers/Category/index.js
new file mode 100644
index 00000000..082d50d4
--- /dev/null
+++ b/src/features/settings/containers/Category/index.js
@@ -0,0 +1,57 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { reduxForm, getFormValues } from 'redux-form';
+import { validate } from './validation';
+import * as CategoryAction from '../../actions';
+import { Category } from '../../components/Category';
+import { CATEGORY_FORM, CATEGORY_ADD } from '../../constants';
+
+const mapStateToProps = (state, { navigation }) => {
+
+ const {
+ global: { language },
+ settings: {
+ loading: {
+ expenseCategoryLoading,
+ initExpenseCategoryLoading,
+ }
+ }
+ } = state
+
+ let type = navigation.getParam('type', CATEGORY_ADD)
+ let onFirstTimeCreateExpense = navigation.getParam('onSelect', null)
+
+ return {
+ categoryLoading: expenseCategoryLoading,
+ getEditCategoryLoading: initExpenseCategoryLoading,
+ type,
+ language,
+ onFirstTimeCreateExpense,
+ formValues: getFormValues(CATEGORY_FORM)(state) || {},
+ };
+};
+
+const mapDispatchToProps = {
+ createCategory: CategoryAction.createExpenseCategory,
+ getEditCategory: CategoryAction.getEditExpenseCategory,
+ editCategory: CategoryAction.editExpenseCategory,
+ removeCategory: CategoryAction.removeExpenseCategory
+};
+
+// Redux Forms
+const addEditPaymentReduxForm = reduxForm({
+ form: CATEGORY_FORM,
+ validate,
+})(Category);
+
+// connect
+const AddEditCategoryContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(addEditPaymentReduxForm);
+
+AddEditCategoryContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default AddEditCategoryContainer;
diff --git a/src/features/settings/containers/Category/validation.js b/src/features/settings/containers/Category/validation.js
new file mode 100644
index 00000000..3aa50360
--- /dev/null
+++ b/src/features/settings/containers/Category/validation.js
@@ -0,0 +1,17 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const { name } = values;
+
+ errors.name = getError(
+ name,
+ ['requiredField'],
+ { fieldName: 'Name' },
+ );
+
+ return errors;
+};
diff --git a/src/features/settings/containers/Company/index.js b/src/features/settings/containers/Company/index.js
new file mode 100644
index 00000000..a1d4c3b7
--- /dev/null
+++ b/src/features/settings/containers/Company/index.js
@@ -0,0 +1,67 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Company } from '../../components/Company';
+import { reduxForm, getFormValues } from 'redux-form';
+import { EDIT_COMPANY } from '../../constants';
+import * as CompanyAction from '../../actions';
+import { validate } from './validation';
+import * as AddressAction from '../../../customers/actions';
+
+
+const mapStateToProps = (state) => {
+ const {
+ settings: {
+ loading: {
+ editCompanyInfoLoading,
+ getCompanyInfoLoading
+ }
+ },
+ global: { language },
+ customers: {
+ countries,
+ loading: {
+ countriesLoading,
+ statesLoading,
+ citiesLoading,
+ }
+ },
+ } = state
+
+ return {
+ formValues: getFormValues(EDIT_COMPANY)(state) || {},
+ language,
+ editCompanyLoading: editCompanyInfoLoading,
+ getCompanyInfoLoading,
+ countries,
+ countriesLoading,
+ statesLoading,
+ citiesLoading
+ };
+};
+
+
+const mapDispatchToProps = {
+ editCompanyInformation: CompanyAction.editCompanyInformation,
+ getCompanyInformation: CompanyAction.getCompanyInformation,
+ getCountries: AddressAction.getCountries,
+ getStates: AddressAction.getStates,
+ getCities: AddressAction.getCities
+};
+
+// Redux Forms
+const CompanyReduxForm = reduxForm({
+ form: EDIT_COMPANY,
+ validate,
+})(Company);
+
+// connect
+const CompanyContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(CompanyReduxForm);
+
+CompanyContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default CompanyContainer;
diff --git a/src/features/settings/containers/Company/validation.js b/src/features/settings/containers/Company/validation.js
new file mode 100644
index 00000000..86fa60ae
--- /dev/null
+++ b/src/features/settings/containers/Company/validation.js
@@ -0,0 +1,14 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const { name, country_id } = values;
+
+ errors.name = getError(name, ['requiredField']);
+ errors.country_id = getError(country_id, ['required']);
+
+ return errors;
+};
diff --git a/src/features/settings/containers/LanguageAndCurrency/index.js b/src/features/settings/containers/LanguageAndCurrency/index.js
new file mode 100644
index 00000000..93bdb27e
--- /dev/null
+++ b/src/features/settings/containers/LanguageAndCurrency/index.js
@@ -0,0 +1,67 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Company } from '../../components/Company';
+import { reduxForm, getFormValues } from 'redux-form';
+import * as PreferencesAction from '../../actions';
+import { LanguageAndCurrency } from '../../components/LanguageAndCurrency';
+import { validate } from './validation'
+import { EDIT_LANGUAGE_AND_CURRENCY } from '../../constants';
+
+const mapStateToProps = (state) => {
+ const {
+ settings: {
+ loading: {
+ getPreferencesLoading,
+ editPreferencesLoading,
+ },
+ preferences
+ },
+ global: { language, currencies }
+ } = state
+
+ let isLoading = getPreferencesLoading || typeof preferences === 'undefined' || preferences === null
+
+
+ return {
+ language,
+ isLoading,
+ currencies,
+ editPreferencesLoading,
+ formValues: getFormValues(EDIT_LANGUAGE_AND_CURRENCY)(state) || {},
+ initialValues: !isLoading ? {
+ currency: preferences.selectedCurrency,
+ language: preferences.selectedLanguage,
+ time_zone: preferences.time_zone,
+ date_format: preferences.carbon_date_format,
+ carbon_date_format: preferences.carbon_date_format,
+ moment_date_format: preferences.moment_date_format,
+ fiscal_year: preferences.fiscal_year
+ } : null
+
+ };
+};
+
+
+const mapDispatchToProps = {
+ getPreferences: PreferencesAction.getPreferences,
+ editPreferences: PreferencesAction.editPreferences,
+ clearPreferences: PreferencesAction.clearPreferences,
+};
+
+// Redux Forms
+const LanguageandCurrencyReduxForm = reduxForm({
+ form: EDIT_LANGUAGE_AND_CURRENCY,
+ validate,
+})(LanguageAndCurrency);
+
+// connect
+const LanguageAndCurrencyContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(LanguageandCurrencyReduxForm);
+
+LanguageAndCurrencyContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default LanguageAndCurrencyContainer;
diff --git a/src/features/settings/containers/LanguageAndCurrency/validation.js b/src/features/settings/containers/LanguageAndCurrency/validation.js
new file mode 100644
index 00000000..387477fe
--- /dev/null
+++ b/src/features/settings/containers/LanguageAndCurrency/validation.js
@@ -0,0 +1,27 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const {
+ currency,
+ language,
+ } = values;
+
+ errors.currency = getError(
+ currency,
+ ['requiredField'],
+ { fieldName: 'Currency' },
+ );
+
+ errors.language = getError(
+ language,
+ ['requiredField'],
+ { fieldName: 'Language' },
+ );
+
+
+ return errors;
+};
diff --git a/src/features/settings/containers/Notification/index.js b/src/features/settings/containers/Notification/index.js
new file mode 100644
index 00000000..957c17fb
--- /dev/null
+++ b/src/features/settings/containers/Notification/index.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { reduxForm } from 'redux-form';
+import { NOTIFICATION } from '../../constants';
+import * as NotificationAction from '../../actions';
+import { validate } from './validation';
+import { Notification } from '../../components/Notification';
+
+const mapStateToProps = (state) => {
+ const {
+ global: { language },
+ settings: {
+ loading: {
+ getSettingItemLoading,
+ editSettingItemLoading
+ }
+ },
+ } = state
+
+ return {
+ language,
+ getSettingItemLoading,
+ editSettingItemLoading
+ };
+};
+
+
+const mapDispatchToProps = {
+ getSettingItem: NotificationAction.getSettingItem,
+ editSettingItem: NotificationAction.editSettingItem
+};
+
+// Redux Forms
+const NotificationReduxForm = reduxForm({
+ form: NOTIFICATION,
+ validate,
+})(Notification);
+
+// connect
+const NotificationContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(NotificationReduxForm);
+
+NotificationContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default NotificationContainer;
diff --git a/src/features/settings/containers/Notification/validation.js b/src/features/settings/containers/Notification/validation.js
new file mode 100644
index 00000000..66138417
--- /dev/null
+++ b/src/features/settings/containers/Notification/validation.js
@@ -0,0 +1,18 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const { notification_email } = values;
+
+ if (notification_email) {
+ errors.notification_email = getError(
+ notification_email,
+ ['emailFormat']
+ );
+ }
+
+ return errors;
+};
diff --git a/src/features/settings/containers/Preferences/index.js b/src/features/settings/containers/Preferences/index.js
new file mode 100644
index 00000000..36390a6b
--- /dev/null
+++ b/src/features/settings/containers/Preferences/index.js
@@ -0,0 +1,70 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { reduxForm, getFormValues } from 'redux-form';
+import { EDIT_PREFERENCES } from '../../constants';
+import * as PreferencesAction from '../../actions';
+import { validate } from './validation';
+import { Preferences } from '../../components/Preferences';
+
+const mapStateToProps = (state) => {
+ const {
+ settings: {
+ loading: {
+ getPreferencesLoading,
+ editPreferencesLoading,
+ getSettingItemLoading,
+ editSettingItemLoading
+ },
+ preferences,
+ },
+ global: { language, currencies }
+ } = state
+
+ let isLoading = getPreferencesLoading || typeof preferences === 'undefined' || preferences === null || getSettingItemLoading
+
+ return {
+ language,
+ isLoading,
+ currencies,
+ editPreferencesLoading,
+ editSettingItemLoading,
+ formValues: getFormValues(EDIT_PREFERENCES)(state) || {},
+ initialValues: !isLoading ? {
+ currency: preferences.selectedCurrency,
+ language: preferences.selectedLanguage,
+ time_zone: preferences.time_zone,
+ date_format: preferences.carbon_date_format,
+ carbon_date_format: preferences.carbon_date_format,
+ moment_date_format: preferences.moment_date_format,
+ fiscal_year: preferences.fiscal_year
+ } : null
+
+ };
+};
+
+
+const mapDispatchToProps = {
+ getPreferences: PreferencesAction.getPreferences,
+ editPreferences: PreferencesAction.editPreferences,
+ clearPreferences: PreferencesAction.clearPreferences,
+ getSettingItem: PreferencesAction.getSettingItem,
+ editSettingItem: PreferencesAction.editSettingItem
+};
+
+// Redux Forms
+const PreferencesReduxForm = reduxForm({
+ form: EDIT_PREFERENCES,
+ validate,
+})(Preferences);
+
+// connect
+const PreferencesContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(PreferencesReduxForm);
+
+PreferencesContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default PreferencesContainer;
diff --git a/src/features/settings/containers/Preferences/validation.js b/src/features/settings/containers/Preferences/validation.js
new file mode 100644
index 00000000..a503ac2f
--- /dev/null
+++ b/src/features/settings/containers/Preferences/validation.js
@@ -0,0 +1,24 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const {
+ time_zone,
+ date_format,
+ } = values;
+
+ errors.time_zone = getError(
+ time_zone,
+ ['requiredField'],
+ );
+
+ errors.date_format = getError(
+ date_format,
+ ['requiredField'],
+ );
+
+ return errors;
+};
diff --git a/src/features/settings/containers/Settings/index.js b/src/features/settings/containers/Settings/index.js
new file mode 100644
index 00000000..9a147d2e
--- /dev/null
+++ b/src/features/settings/containers/Settings/index.js
@@ -0,0 +1,34 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { Settings } from '../../components/Settings';
+import { colors } from '../../../../styles/colors';
+import { reduxForm } from 'redux-form';
+import { SETTINGS_SEARCH } from '../../constants';
+import * as SettingAction from '../../actions';
+
+const mapStateToProps = ({ settings, global }) => ({
+ loading: settings.loading.logoutLoading,
+ language: global.language
+});
+
+const mapDispatchToProps = {
+ logout: SettingAction.logout
+};
+
+// Redux Forms
+const settingSearchReduxForm = reduxForm({
+ form: SETTINGS_SEARCH,
+})(Settings);
+
+// connect
+const SettingContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(settingSearchReduxForm);
+
+SettingContainer.navigationOptions = () => ({
+ header: null
+});
+
+export default SettingContainer;
diff --git a/src/features/settings/containers/Tax/index.js b/src/features/settings/containers/Tax/index.js
new file mode 100644
index 00000000..009c1b85
--- /dev/null
+++ b/src/features/settings/containers/Tax/index.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Tax } from '../../components/Tax';
+import { reduxForm } from 'redux-form';
+import * as TaxAction from '../../actions';
+import { validate } from './validation';
+import { TAX_FORM, ADD_TAX } from '../../constants';
+
+const mapStateToProps = ({ settings, global }, { navigation }) => {
+
+ const taxType = navigation.getParam('tax', {})
+ const type = navigation.getParam('type', ADD_TAX)
+
+ const isLoading = settings.loading.editTaxLoading || settings.loading.addTaxLoading || settings.loading.removeTaxLoading
+
+
+ return {
+ loading: isLoading,
+ type,
+ taxId: taxType && taxType.id,
+ language: global.language,
+ initialValues: {
+ collective_tax: 0,
+ compound_tax: 0,
+ ...taxType
+ }
+ }
+};
+
+const mapDispatchToProps = {
+ addTax: TaxAction.addTax,
+ editTax: TaxAction.editTax,
+ removeTax: TaxAction.removeTax,
+};
+
+// Redux Forms
+const TaxReduxForm = reduxForm({
+ form: TAX_FORM,
+ validate,
+})(Tax);
+
+// connect
+const TaxContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(TaxReduxForm);
+
+TaxContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default TaxContainer;
diff --git a/src/features/settings/containers/Tax/validation.js b/src/features/settings/containers/Tax/validation.js
new file mode 100644
index 00000000..f403c2f7
--- /dev/null
+++ b/src/features/settings/containers/Tax/validation.js
@@ -0,0 +1,18 @@
+import { getError } from "../../../../api/validation";
+
+// @flow
+
+
+export const validate = (values) => {
+ const errors = {};
+ const { name, percent } = values;
+
+ errors.name = getError(name, ['requiredField']);
+ errors.percent = getError(
+ percent,
+ ['requiredField', 'isNumberFormat', 'maxNumberRequired', 'minNumberRequired'],
+ options = { fieldName: 'Tax Percent', maxNumber: 100, minNumber: 0 }
+ );
+
+ return errors;
+};
diff --git a/src/features/settings/containers/Taxes/index.js b/src/features/settings/containers/Taxes/index.js
new file mode 100644
index 00000000..630b8236
--- /dev/null
+++ b/src/features/settings/containers/Taxes/index.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Taxes } from '../../components/Taxes';
+import { reduxForm } from 'redux-form';
+import { SEARCH_TAX } from '../../constants';
+import * as TaxesAction from '../../actions';
+
+const mapStateToProps = ({ settings, global }) => ({
+ loading: settings.loading.getTaxLoading,
+ taxTypes: global.taxTypes,
+ language: global.language
+});
+
+const mapDispatchToProps = {
+ getTaxes: TaxesAction.getTaxes
+};
+
+// Redux Forms
+const TaxesReduxForm = reduxForm({
+ form: SEARCH_TAX,
+})(Taxes);
+
+// connect
+const TaxesContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(TaxesReduxForm);
+
+TaxesContainer.navigationOptions = () => ({
+ header: null,
+});
+
+export default TaxesContainer;
diff --git a/src/features/settings/reducers/index.js b/src/features/settings/reducers/index.js
new file mode 100644
index 00000000..b395bbc9
--- /dev/null
+++ b/src/features/settings/reducers/index.js
@@ -0,0 +1,121 @@
+import {
+ SETTINGS_TRIGGER_SPINNER,
+ SET_COMPANY_INFO,
+ SET_ACCOUNT_INFO,
+ SET_PREFERENCES,
+ CLEAR_PREFERENCES,
+ SET_SETTING_ITEM,
+ SET_CREATE_EXPENSE_CATEGORIES,
+ SET_EDI_EXPENSE_CATEGORIES,
+ SET_REMOVE_EXPENSE_CATEGORIES,
+ SET_EXPENSE_CATEGORIES,
+} from '../constants';
+import { env } from '../../../config';
+
+const initialState = {
+ loading: {
+ logoutLoading: false,
+ // account
+ getAccountInfoLoading: false,
+ editAccountInfoLoading: false,
+ // company
+ getCompanyInfoLoading: false,
+ editCompanyInfoLoading: false,
+ // preferences
+ getPreferencesLoading: false,
+ editPreferencesLoading: false,
+ // item
+ getSettingItemLoading: false,
+ setSettingItemLoading: false,
+ editSettingItemLoading: false,
+ // categories
+ expensesCategoryLoading: false,
+ expenseCategoryLoading: false,
+ initExpenseCategoryLoading: false,
+ // taxes
+ addTaxLoading: false,
+ getTaxLoading: false,
+ removeTaxLoading: false,
+ },
+ preferences: null,
+ company: {
+ addresses: [],
+ company: {}
+ },
+ categories: [],
+ account: null,
+ taxByItems: false,
+ taxByInvoice: true,
+ taxByEstimate: false,
+};
+
+export default function authReducer(state = initialState, action) {
+ const { payload, type } = action;
+
+ switch (type) {
+ case SETTINGS_TRIGGER_SPINNER:
+ return { ...state, loading: { ...payload } };
+
+ case SET_COMPANY_INFO:
+ return { ...state, ...payload };
+
+ case SET_ACCOUNT_INFO:
+ return { ...state, ...payload };
+
+ case SET_PREFERENCES:
+ return { ...state, ...payload }
+
+ case CLEAR_PREFERENCES:
+ return { ...state, preferences: null };
+
+ case SET_SETTING_ITEM:
+ const { key, value } = payload
+ if (key === 'discount_per_item') {
+
+ return { ...state, discountPerItem: value };
+ }
+
+ if (key === 'tax_per_item')
+ return { ...state, taxPerItem: value };
+ else
+ return { ...state, ...payload }
+
+ case SET_EXPENSE_CATEGORIES:
+
+ return { ...state, ...payload };
+
+ case SET_CREATE_EXPENSE_CATEGORIES:
+
+ return {
+ ...state, categories:
+ [...payload.categories, ...state.categories]
+ };
+
+ case SET_EDI_EXPENSE_CATEGORIES:
+
+ let itemIndex = 0;
+
+ state.categories.map((val, index) => {
+ if (val.id === payload.id)
+ itemIndex = index
+ })
+
+ state.categories.splice(itemIndex, 1)
+
+ return {
+ ...state,
+ categories: [...payload.categories, ...state.categories]
+ }
+
+ case SET_REMOVE_EXPENSE_CATEGORIES:
+
+ const category = state.categories.filter((val) =>
+ (val.id !== payload.id))
+
+ return { ...state, categories: category };
+
+
+ default:
+ return state;
+ }
+}
diff --git a/src/features/settings/saga/index.js b/src/features/settings/saga/index.js
new file mode 100644
index 00000000..b49e7143
--- /dev/null
+++ b/src/features/settings/saga/index.js
@@ -0,0 +1,559 @@
+import { call, put, takeEvery, takeLatest } from 'redux-saga/effects';
+
+import {
+ settingsTriggerSpinner,
+ setCompanyInformation,
+ setAccountInformation,
+ setPreferences,
+ setSettings,
+ setExpenseCategories,
+ setCreateExpenseCategories,
+ setEditExpenseCategories,
+ setRemoveExpenseCategories,
+ setTaxes,
+ setTax,
+ setEditTax,
+ setRemoveTax,
+} from '../actions';
+
+import {
+ GET_COMPANY_INFO,
+ EDIT_COMPANY_INFO,
+ GET_ACCOUNT_INFO,
+ EDIT_ACCOUNT_INFO,
+ GET_PREFERENCES,
+ EDIT_PREFERENCES,
+ GET_SETTING_ITEM,
+ EDIT_SETTING_ITEM,
+ GET_EXPENSE_CATEGORIES,
+ CREATE_EXPENSE_CATEGORY,
+ GET_CREATE_EXPENSE_CATEGORY,
+ EDIT_EXPENSE_CATEGORY,
+ REMOVE_EXPENSE_CATEGORY,
+ GET_TAXES,
+ REMOVE_TAX,
+ TAX_ADD,
+ TAX_EDIT,
+ // Endpoint Api URL
+ GET_COMPANY_URL,
+ EDIT_COMPANY_URL,
+ GET_ACCOUNT_URL,
+ EDIT_ACCOUNT_URL,
+ GET_PREFERENCES_URL,
+ GET_GENERAL_SETTING_URL,
+ EDIT_PREFERENCES_URL,
+ EDIT_GENERAL_SETTING_URL,
+ GET_EXPENSE_CATEGORIES_URL,
+ CREATE_EXPENSE_CATEGORIES_URL,
+ GET_EDIT_EXPENSE_CATEGORIES_URL,
+ EDIT_EXPENSE_CATEGORIES_URL,
+ REMOVE_EXPENSE_CATEGORIES_URL,
+ GET_SALES_TAXES_URL,
+ CREATE_SALES_TAX_URL,
+ EDIT_SALES_TAX_URL,
+ REMOVE_SALES_TAX_URL
+} from '../constants';
+
+import Request from '../../../api/request';
+import { ROUTES } from '../../../navigation/routes';
+
+/**
+ * Company Information.
+ */
+function* getCompanyInformation(payloadData) {
+ const {
+ payload: { onResult },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ getCompanyInfoLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_COMPANY_URL(),
+ };
+
+ const response = yield call([Request, 'get'], options);
+ yield put(setCompanyInformation({ company: response.user }));
+ onResult && onResult(response.user)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ getCompanyInfoLoading: false }));
+ }
+}
+
+function* editCompanyInformation(payloadData) {
+ const {
+ payload: { params, navigation, logo },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ editCompanyInfoLoading: true }));
+
+ try {
+
+ const options = {
+ path: EDIT_COMPANY_URL(),
+ body: params,
+ };
+
+ yield call([Request, 'post'], options);
+
+ if (logo) {
+ const options2 = {
+ path: `settings/company/upload-logo`,
+ image: logo,
+ imageName: 'company_logo'
+ }
+
+ yield call([Request, 'post'], options2);
+ }
+
+ navigation.goBack(null)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ editCompanyInfoLoading: false }));
+ }
+}
+
+function* getAccountInformation(payloadData) {
+
+ yield put(settingsTriggerSpinner({ getAccountInfoLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_ACCOUNT_URL(),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setAccountInformation({ account: response }));
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ getAccountInfoLoading: false }));
+ }
+}
+
+function* editAccountInformation(payloadData) {
+ const {
+ payload: { params, navigation },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ editAccountInfoLoading: true }));
+
+ try {
+
+ const options = {
+ path: EDIT_ACCOUNT_URL(),
+ body: params
+ };
+
+ const response = yield call([Request, 'put'], options);
+ yield put(setAccountInformation({ account: response.user }));
+ navigation.goBack(null)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ editAccountInfoLoading: false }));
+ }
+}
+
+/**
+ * App Settings
+ * -----------------------------------------
+ */
+function* getPreferences(payloadData) {
+ const {
+ payload: { onResult },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ getPreferencesLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_PREFERENCES_URL(),
+ };
+
+ const response = yield call([Request, 'get'], options);
+ yield put(setPreferences({ preferences: response }));
+ onResult && onResult(response)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ getPreferencesLoading: false }));
+ }
+}
+
+function* getSettingItem(payloadData) {
+ const {
+ payload: { key, onResult = null },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ getSettingItemLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_GENERAL_SETTING_URL(key),
+ };
+
+ const response = yield call([Request, 'get'], options);
+ onResult && onResult(response[key])
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ getSettingItemLoading: false }));
+ }
+}
+
+function* editPreferences(payloadData) {
+ const {
+ payload: { params, navigation, currencies },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ editPreferencesLoading: true }));
+
+ try {
+
+ const options = {
+ path: EDIT_PREFERENCES_URL(),
+ body: params
+ };
+
+ const response = yield call([Request, 'put'], options);
+ if (response.success) {
+ let newData = currencies.filter((item) => {
+ let filterData = false
+ if (item['id'].toString() === params.currency.toString())
+ filterData = true
+
+ return filterData
+ });
+
+ yield put(setSettings({
+ settings: params,
+ currency: newData.length !== 0 ? newData[0] : []
+ }));
+ navigation.goBack(null)
+ }
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ editPreferencesLoading: false }));
+ }
+}
+
+function* editSettingItem(payloadData) {
+ const {
+ payload: { params, navigation = null, onResult = null },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ editSettingItemLoading: true }));
+
+ try {
+
+ const options = {
+ path: EDIT_GENERAL_SETTING_URL(),
+ body: params
+ };
+
+ const response = yield call([Request, 'put'], options);
+
+ if (response.success) {
+ yield put(setSettings({ settings: params }));
+ onResult && onResult()
+ }
+
+ if (navigation)
+ navigation.goBack(null)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ editSettingItemLoading: false }));
+ }
+}
+
+/**
+ * Expense Categories
+ */
+function* getExpenseCategories(payloadData) {
+
+ yield put(settingsTriggerSpinner({ expensesCategoryLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_EXPENSE_CATEGORIES_URL(),
+ };
+
+ const response = yield call([Request, 'get'], options);
+ yield put(setExpenseCategories({ categories: response.categories }));
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ expensesCategoryLoading: false }));
+ }
+}
+
+function* createExpenseCategory(payloadData) {
+ const {
+ payload: { params, onResult },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ expenseCategoryLoading: true }));
+
+ try {
+
+ const options = {
+ path: CREATE_EXPENSE_CATEGORIES_URL(),
+ body: params
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ yield put(setCreateExpenseCategories({ categories: [response.category] }));
+
+ onResult && onResult(response.category)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ expenseCategoryLoading: false }));
+ }
+}
+
+function* getEditExpenseCategory(payloadData) {
+ const {
+ payload: { id, onResult },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ initExpenseCategoryLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_EDIT_EXPENSE_CATEGORIES_URL(id),
+ };
+
+ const response = yield call([Request, 'get'], options);
+ onResult && onResult(response.category)
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ initExpenseCategoryLoading: false }));
+ }
+}
+
+function* editExpenseCategory(payloadData) {
+ const {
+ payload: { id, params, navigation },
+ } = payloadData;
+
+
+ yield put(settingsTriggerSpinner({ expenseCategoryLoading: true }));
+
+ try {
+
+ const options = {
+ path: EDIT_EXPENSE_CATEGORIES_URL(id),
+ body: params
+ };
+
+ const response = yield call([Request, 'put'], options);
+ navigation.navigate(ROUTES.CATEGORIES)
+ yield put(setEditExpenseCategories({ categories: [response.category], id }));
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ expenseCategoryLoading: false }));
+ }
+}
+
+function* removeExpenseCategory(payloadData) {
+ const {
+ payload: { id, navigation, onResult },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ expenseCategoryLoading: true }));
+
+ try {
+
+ const options = {
+ path: REMOVE_EXPENSE_CATEGORIES_URL(id),
+ };
+
+ const response = yield call([Request, 'delete'], options);
+
+ if (response.success) {
+ navigation.navigate(ROUTES.CATEGORIES)
+ yield put(setRemoveExpenseCategories({ id }));
+ }
+ else
+ onResult && onResult()
+
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ expenseCategoryLoading: false }));
+ }
+}
+
+/**
+ * Tax Types
+ */
+function* getTaxTypes(payloadData) {
+ const {
+ payload: {
+ onResult,
+ } = {},
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ getTaxesLoading: true }));
+
+ try {
+
+ const options = {
+ path: GET_SALES_TAXES_URL(),
+ };
+
+ const response = yield call([Request, 'get'], options);
+
+ yield put(setTaxes({ taxTypes: response.taxTypes }));
+
+ onResult && onResult(response);
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ getTaxesLoading: false }));
+ }
+}
+
+function* addTax(payloadData) {
+ const {
+ payload: {
+ tax,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ addTaxLoading: true }));
+
+ try {
+
+ const options = {
+ path: CREATE_SALES_TAX_URL(),
+ body: tax
+ };
+
+ const response = yield call([Request, 'post'], options);
+
+ yield put(setTax({ taxType: [response.taxType] }));
+
+ onResult && onResult(response.taxType);
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ addTaxLoading: false }));
+ }
+}
+
+function* editTaxType(payloadData) {
+ const {
+ payload: {
+ tax,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ editTaxLoading: true }));
+
+ try {
+
+ const options = {
+ path: EDIT_SALES_TAX_URL(tax),
+ body: tax
+ };
+
+ const response = yield call([Request, 'put'], options);
+
+ yield put(setEditTax({ taxType: [response.taxType], taxId: tax.id }));
+
+ onResult && onResult(response);
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ editTaxLoading: false }));
+ }
+}
+
+function* removeTax(payloadData) {
+ const {
+ payload: {
+ id,
+ onResult,
+ },
+ } = payloadData;
+
+ yield put(settingsTriggerSpinner({ removeTaxLoading: true }));
+
+ try {
+
+ const options = {
+ path: REMOVE_SALES_TAX_URL(id),
+ };
+
+ const response = yield call([Request, 'delete'], options);
+
+ if (response.success)
+ yield put(setRemoveTax({ taxId: id }));
+
+ onResult && onResult(response.success);
+ } catch (error) {
+ // console.log(error);
+ } finally {
+ yield put(settingsTriggerSpinner({ removeTaxLoading: false }));
+
+ }
+}
+
+
+export default function* settingsSaga() {
+ yield takeEvery(GET_COMPANY_INFO, getCompanyInformation);
+ yield takeEvery(EDIT_COMPANY_INFO, editCompanyInformation);
+ yield takeEvery(GET_ACCOUNT_INFO, getAccountInformation);
+ yield takeEvery(EDIT_ACCOUNT_INFO, editAccountInformation);
+ yield takeEvery(GET_PREFERENCES, getPreferences);
+ yield takeEvery(GET_SETTING_ITEM, getSettingItem);
+ yield takeEvery(EDIT_PREFERENCES, editPreferences);
+ yield takeEvery(EDIT_SETTING_ITEM, editSettingItem);
+
+ // Expense Categories
+ // -----------------------------------------
+ yield takeEvery(GET_EXPENSE_CATEGORIES, getExpenseCategories);
+ yield takeEvery(CREATE_EXPENSE_CATEGORY, createExpenseCategory);
+ yield takeEvery(GET_CREATE_EXPENSE_CATEGORY, getEditExpenseCategory);
+ yield takeEvery(EDIT_EXPENSE_CATEGORY, editExpenseCategory);
+ yield takeEvery(REMOVE_EXPENSE_CATEGORY, removeExpenseCategory);
+
+ // Tax Types
+ // -----------------------------------------
+ yield takeEvery(GET_TAXES, getTaxTypes);
+ yield takeEvery(TAX_ADD, addTax);
+ yield takeEvery(TAX_EDIT, editTaxType);
+ yield takeEvery(REMOVE_TAX, removeTax);
+}
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 00000000..20c4b99a
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,73 @@
+import React, { Component } from 'react';
+import { View } from 'react-native';
+import { Provider } from 'react-redux';
+import { PersistGate } from 'redux-persist/integration/react';
+import { NavigationActions } from 'react-navigation';
+import { store, persistor } from './store';
+import { loadFonts } from './api/global';
+import ApplicationNavigator from "./navigation/containers";
+import { getBootstrap, getAppVersion } from './features/authentication/actions';
+import { AppLoader } from './components';
+import compareVersion from './api/compareVersion';
+import { ROUTES } from './navigation/routes';
+import { env } from './config';
+
+console.disableYellowBox = true;
+
+export default class Root extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ fontLoaded: false,
+ };
+ }
+
+ componentWillMount() {
+ loadFonts({
+ afterLoad: () => {
+ const reduxStore = store.getState();
+
+ let { idToken = null } = reduxStore.auth;
+ let { endpointApi = null } = reduxStore.global;
+
+ if (idToken) {
+ store.dispatch(getBootstrap())
+ }
+
+ if (endpointApi) {
+ (endpointApi !== null && typeof endpointApi !== 'undefined') &&
+ store.dispatch(getAppVersion({
+ onResult: ({ version }) => {
+ if (version && (compareVersion(env.APP_VERSION, version) < 0)) {
+ store.dispatch(
+ NavigationActions.navigate({
+ routeName: ROUTES.UPDATE_APP_VERSION,
+ }),
+ );
+ }
+ }
+ }))
+ }
+ this.setState({ fontLoaded: true });
+ },
+ });
+ }
+
+ render() {
+ const { fontLoaded } = this.state;
+
+ return (
+
+
+ {fontLoaded && (
+
+
+
+
+ )}
+
+
+ );
+ }
+}
diff --git a/src/navigation/actions/index.js b/src/navigation/actions/index.js
new file mode 100644
index 00000000..5cf49920
--- /dev/null
+++ b/src/navigation/actions/index.js
@@ -0,0 +1,151 @@
+
+import { BackHandler } from 'react-native';
+import { NavigationActions, createStackNavigator } from "react-navigation";
+import { ROUTES } from "../routes";
+import { store } from '../../store';
+import Lng from '../../api/lang/i18n';
+
+
+export const navigateBack = () => NavigationActions.back();
+
+export const navigateToMainTabs = () => {
+ NavigationActions.navigate({
+ routeName: ROUTES.MAIN_TABS
+ });
+}
+export const navigateTo = (routeName) => {
+ NavigationActions.navigate({ routeName });
+}
+
+
+// Go Back Navigation
+// -----------------------------------------
+
+
+export const MOUNT = 'mount'
+export const UNMOUNT = 'unMount'
+export const ANDROID_BACK = 'ANDROID_BACK'
+
+export const goBackWithFunction = (param, navigation = {}, args = '') => {
+
+ if (param === MOUNT) {
+ this.backHandler = BackHandler.addEventListener('hardwareBackPress',
+ () => {
+
+ let currentRoute = getCurrentRouteName()
+
+ // Estimates
+ currentRoute === ROUTES.ESTIMATE && args()
+ currentRoute === ROUTES.ESTIMATE_LIST && navigation.navigate(ROUTES.MAIN_MORE)
+
+ // Invoices
+ currentRoute === ROUTES.INVOICE && args()
+ currentRoute === ROUTES.MAIN_INVOICES && navigation.navigate(ROUTES.MAIN_INVOICES)
+
+ return true;
+ }
+ )
+ } else {
+ this.backHandler.remove()
+ }
+
+}
+
+export const goBack = (param, navigation = {}, route = '') => {
+
+ if (param === MOUNT) {
+ this.backHandler = BackHandler.addEventListener('hardwareBackPress',
+ () => {
+ route && typeof route === 'string' ? navigation.navigate(route)
+ : navigation.goBack(null);
+
+ return true;
+ })
+ } else {
+ this.backHandler.remove()
+ }
+
+}
+
+export const getCurrentRouteName = () => {
+ const reduxStore = store.getState();
+ const { routes } = reduxStore.nav
+ const currentRoteBlock = routes[routes.length - 1];
+ return currentRoteBlock.routeName;
+}
+
+
+export const generateStackNavigation = (route, screen) => createStackNavigator(
+ {
+ [route]: screen,
+ },
+ {
+ initialRouteName: route,
+ navigationOptions: {
+ header: null,
+ headerTransparent: true,
+ },
+ }
+);
+
+// Navigate Specific Route
+// -----------------------------------------
+export const navigateRoute = (routeName, params = {}) => {
+ store.dispatch(
+ NavigationActions.navigate({
+ routeName,
+ params
+ }),
+ );
+}
+
+export const navigateTabRoutes = (exceptRouteName, params = {}) => {
+ let routes = [
+ ROUTES.MAIN_INVOICES,
+ ROUTES.MAIN_CUSTOMERS,
+ ROUTES.MAIN_PAYMENTS,
+ ROUTES.MAIN_EXPENSES
+ ]
+
+ routes.map(route => {
+ !(route === exceptRouteName) && store.dispatch(
+ NavigationActions.navigate({
+ routeName: route,
+ params
+ }),
+ );
+ })
+
+}
+
+// onPress BottomTabNavigator
+// -----------------------------------------
+export const tabBarOnPress = (routeName = '', action = '') => {
+
+ routeName && navigateRoute(routeName, { loading: true })
+
+ let activeTabIfInvoices = ''
+
+ if (routeName === ROUTES.MAIN_INVOICES) {
+ const reduxStore = store.getState();
+ activeTabIfInvoices = reduxStore.invoices.activeTab
+ }
+
+ store.dispatch(action({
+ type: activeTabIfInvoices,
+ onMeta: ({ last_page, current_page }) => {
+ let pagination = { last_page, current_page }
+ routeName && navigateRoute(routeName, { pagination, loading: false })
+ }
+ }))
+}
+
+
+// Get Value with translated
+// -----------------------------------------
+export const getTitleByLanguage = (label) => {
+ const reduxStore = store.getState();
+ const { language = 'en' } = reduxStore.global
+
+ return Lng.t(label, { locale: language })
+}
\ No newline at end of file
diff --git a/src/navigation/containers/index.js b/src/navigation/containers/index.js
new file mode 100644
index 00000000..f81c9716
--- /dev/null
+++ b/src/navigation/containers/index.js
@@ -0,0 +1,17 @@
+import React, { Component } from "react";
+import { connect } from "react-redux";
+import ApplicationNavigator from "../navigators";
+import { createReduxContainer } from 'react-navigation-redux-helpers';
+
+const mapStateToProps = ({ navigationData, nav}) => ({
+ navigation: navigationData,
+ state: nav
+});
+
+const mapDispatchToProps = (dispatch) => ({ dispatch });
+
+const appNavigator = createReduxContainer(ApplicationNavigator, 'root')
+
+export default connect(mapStateToProps, mapDispatchToProps)(
+ appNavigator
+);
diff --git a/src/navigation/navigators/auth.js b/src/navigation/navigators/auth.js
new file mode 100644
index 00000000..daf45fa1
--- /dev/null
+++ b/src/navigation/navigators/auth.js
@@ -0,0 +1,39 @@
+import { createStackNavigator } from "react-navigation";
+import LoginContainer from '../../features/authentication/containers/Login';
+import ForgotPasswordContainer from '../../features/authentication/containers/ForgetPassword';
+import EndpointContainer from "../../features/authentication/containers/Endpoint";
+import UpdateAppVersionContainer from "../../components/UpdateAppVersion";
+import { ROUTES } from "../routes";
+import { generateStackNavigation } from "../actions";
+
+export default createStackNavigator(
+ {
+ [ROUTES.LOGIN]: {
+ screen: LoginContainer
+ },
+ [ROUTES.FORGOT_PASSWORD]: {
+ screen: ForgotPasswordContainer
+ },
+
+ // Endpoint Api
+ // -----------------------------------------
+ [ROUTES.ENDPOINTS]: generateStackNavigation(
+ ROUTES.ENDPOINTS,
+ EndpointContainer,
+ ),
+
+ // Update App Version
+ // -----------------------------------------
+ [ROUTES.UPDATE_APP_VERSION]: generateStackNavigation(
+ ROUTES.UPDATE_APP_VERSION,
+ UpdateAppVersionContainer,
+ ),
+ },
+ {
+ initialRouteName: ROUTES.LOGIN,
+ navigationOptions: {
+ header: null,
+ headerTransparent: true,
+ },
+ },
+);
diff --git a/src/navigation/navigators/customer.js b/src/navigation/navigators/customer.js
new file mode 100644
index 00000000..c59376e4
--- /dev/null
+++ b/src/navigation/navigators/customer.js
@@ -0,0 +1,13 @@
+
+import { ROUTES } from "../routes";
+import { generateStackNavigation } from "../actions";
+import CustomerContainer from "../../features/customers/containers/Customer";
+
+export const CustomerNavigator = {
+
+ [ROUTES.CUSTOMER]: generateStackNavigation(
+ ROUTES.CUSTOMER,
+ CustomerContainer,
+ ),
+}
+
diff --git a/src/navigation/navigators/estimate.js b/src/navigation/navigators/estimate.js
new file mode 100644
index 00000000..50266eb8
--- /dev/null
+++ b/src/navigation/navigators/estimate.js
@@ -0,0 +1,21 @@
+import EstimatesContainer from '../../features/estimates/containers/Estimates';
+import EstimateContainer from '../../features/estimates/containers/Estimate';
+import EstimateItemContainer from '../../features/estimates/containers/Item';
+import { ROUTES } from "../routes";
+import { generateStackNavigation } from "../actions";
+
+export const EstimateNavigator = {
+
+ [ROUTES.ESTIMATE_LIST]: generateStackNavigation(
+ ROUTES.ESTIMATE_LIST,
+ EstimatesContainer,
+ ),
+ [ROUTES.ESTIMATE]: generateStackNavigation(
+ ROUTES.ESTIMATE,
+ EstimateContainer,
+ ),
+ [ROUTES.ESTIMATE_ITEM]: generateStackNavigation(
+ ROUTES.ESTIMATE_ITEM,
+ EstimateItemContainer,
+ ),
+}
diff --git a/src/navigation/navigators/expense.js b/src/navigation/navigators/expense.js
new file mode 100644
index 00000000..3b401549
--- /dev/null
+++ b/src/navigation/navigators/expense.js
@@ -0,0 +1,14 @@
+
+import { ROUTES } from "../routes";
+import ExpenseContainer from "../../features/expenses/containers/Expense";
+import { generateStackNavigation } from "../actions";
+
+export const ExpenseNavigator = {
+
+ [ROUTES.EXPENSE]: generateStackNavigation(
+ ROUTES.EXPENSE,
+ ExpenseContainer,
+ )
+
+}
+
diff --git a/src/navigation/navigators/index.js b/src/navigation/navigators/index.js
new file mode 100644
index 00000000..c0be26db
--- /dev/null
+++ b/src/navigation/navigators/index.js
@@ -0,0 +1,59 @@
+import React from "react";
+import { createStackNavigator } from "react-navigation";
+import MainNavigator from "./main";
+
+import { ROUTES } from "../routes";
+import { PaymentNavigator } from "./payment";
+import { CustomerNavigator } from "./customer";
+import AuthNavigator from "./auth";
+import { InvoiceNavigator } from "./invoice";
+import { EstimateNavigator } from "./estimate";
+import { ExpenseNavigator } from "./expense";
+import { SettingNavigator } from "./settings";
+import { MoreNavigator } from "./more";
+
+const appNavigator = createStackNavigator(
+ {
+ // Auth
+ [ROUTES.AUTH]: {
+ screen: AuthNavigator
+ },
+
+ // MainTab
+ [ROUTES.MAIN_TABS]: {
+ screen: MainNavigator
+ },
+
+ // Invoice
+ ...InvoiceNavigator,
+
+ // Estimate
+ ...EstimateNavigator,
+
+ // Expense
+ ...ExpenseNavigator,
+
+ // Setting
+ ...SettingNavigator,
+
+ // Customer
+ ...CustomerNavigator,
+
+ // Payments
+ ...PaymentNavigator,
+
+ // More
+ ...MoreNavigator,
+ },
+ {
+ initialRouteName: ROUTES.AUTH,
+ navigationOptions: {
+ header: null,
+ headerTransparent: true,
+ gesturesEnabled: false,
+ headerTitleAllowFontScaling: false,
+ },
+ },
+);
+
+export default appNavigator;
diff --git a/src/navigation/navigators/invoice.js b/src/navigation/navigators/invoice.js
new file mode 100644
index 00000000..525a32fc
--- /dev/null
+++ b/src/navigation/navigators/invoice.js
@@ -0,0 +1,16 @@
+import InvoiceContainer from '../../features/invoices/containers/Invoice';
+import InvoiceItemContainer from '../../features/invoices/containers/Item';
+import { ROUTES } from "../routes";
+import { generateStackNavigation } from "../actions";
+
+export const InvoiceNavigator = {
+
+ [ROUTES.INVOICE]: generateStackNavigation(
+ ROUTES.INVOICE,
+ InvoiceContainer,
+ ),
+ [ROUTES.INVOICE_ITEM]: generateStackNavigation(
+ ROUTES.INVOICE_ITEM,
+ InvoiceItemContainer,
+ ),
+}
diff --git a/src/navigation/navigators/main.js b/src/navigation/navigators/main.js
new file mode 100644
index 00000000..bd4cbff8
--- /dev/null
+++ b/src/navigation/navigators/main.js
@@ -0,0 +1,56 @@
+import { createBottomTabNavigator } from "react-navigation";
+import InvoicesContainer from '../../features/invoices/containers/Invoices';
+import CustomersContainer from '../../features/customers/containers/Customers';
+import { ROUTES } from "../routes";
+import { isIPhoneX } from "../../api/helper";
+import { fonts } from "../../styles/fonts";
+import { colors } from "../../styles/colors";
+import MoreContainer from "../../features/more/containers/More";
+import ExpensesContainer from "../../features/expenses/containers/Expenses";
+import PaymentsContainer from "../../features/payments/containers/Payments";
+
+export default createBottomTabNavigator(
+ {
+ [ROUTES.MAIN_INVOICES]: {
+ screen: InvoicesContainer
+ },
+ [ROUTES.MAIN_CUSTOMERS]: {
+ screen: CustomersContainer
+ },
+ [ROUTES.MAIN_PAYMENTS]: {
+ screen: PaymentsContainer
+ },
+ [ROUTES.MAIN_EXPENSES]: {
+ screen: ExpensesContainer
+ },
+ [ROUTES.MAIN_MORE]: {
+ screen: MoreContainer
+ }
+ },
+ {
+ initialRouteName: ROUTES.MAIN_INVOICES,
+ navigationOptions: {
+ header: null,
+ headerTitleAllowFontScaling: false,
+ },
+ tabBarOptions: {
+ showIcon: true,
+ showLabel: true,
+ activeTintColor: colors.primary,
+ inactiveTintColor: colors.dark2,
+ allowFontScaling: false,
+ style: {
+ backgroundColor: colors.white,
+ width: '100%',
+ height: isIPhoneX() ? 65 : 75,
+ borderTopWidth: 0,
+ paddingVertical: 7,
+ },
+ labelStyle: {
+ fontFamily: fonts.poppinsMedium,
+ fontSize: 11,
+ marginTop: -4,
+ },
+ }
+ }
+);
diff --git a/src/navigation/navigators/more.js b/src/navigation/navigators/more.js
new file mode 100644
index 00000000..b6e75e89
--- /dev/null
+++ b/src/navigation/navigators/more.js
@@ -0,0 +1,42 @@
+
+import { ROUTES } from "../routes";
+import { generateStackNavigation } from "../actions";
+import ItemsContainer from "../../features/more/containers/Items";
+import ItemContainer from "../../features/more/containers/Item";
+import ReportsContainer from "../../features/more/containers/Reports";
+import ReportContainer from "../../features/more/containers/Report";
+import LostConnectionContainer from "../../components/LostConnection";
+
+export const MoreNavigator = {
+
+ // Items
+ // -----------------------------------------
+ [ROUTES.GLOBAL_ITEMS]: generateStackNavigation(
+ ROUTES.GLOBAL_ITEMS,
+ ItemsContainer,
+ ),
+
+ [ROUTES.GLOBAL_ITEM]: generateStackNavigation(
+ ROUTES.GLOBAL_ITEM,
+ ItemContainer,
+ ),
+
+ // Reports
+ // -----------------------------------------
+ [ROUTES.REPORTS]: generateStackNavigation(
+ ROUTES.REPORTS,
+ ReportsContainer,
+ ),
+
+ [ROUTES.GENERATE_REPORT]: generateStackNavigation(
+ ROUTES.GENERATE_REPORT,
+ ReportContainer,
+ ),
+
+ // Lost Connection
+ // -----------------------------------------
+ [ROUTES.LOST_CONNECTION]: generateStackNavigation(
+ ROUTES.LOST_CONNECTION,
+ LostConnectionContainer,
+ ),
+}
diff --git a/src/navigation/navigators/payment.js b/src/navigation/navigators/payment.js
new file mode 100644
index 00000000..04d456fd
--- /dev/null
+++ b/src/navigation/navigators/payment.js
@@ -0,0 +1,17 @@
+
+import { ROUTES } from "../routes";
+import { generateStackNavigation } from "../actions";
+import PaymentContainer from "../../features/payments/containers/Payment";
+
+
+export const PaymentNavigator = {
+
+ [ROUTES.PAYMENT]: generateStackNavigation(
+ ROUTES.PAYMENT,
+ PaymentContainer,
+ ),
+
+}
+
+
+
diff --git a/src/navigation/navigators/settings.js b/src/navigation/navigators/settings.js
new file mode 100644
index 00000000..39d2de43
--- /dev/null
+++ b/src/navigation/navigators/settings.js
@@ -0,0 +1,74 @@
+
+import { ROUTES } from "../routes";
+import SettingsContainer from "../../features/settings/containers/Settings";
+import AccountContainer from "../../features/settings/containers/Account";
+import CompanyContainer from "../../features/settings/containers/Company";
+import LanguageAndCurrencyContainer from "../../features/settings/containers/LanguageAndCurrency";
+import { generateStackNavigation } from "../actions";
+import NotificationContainer from "../../features/settings/containers/Notification";
+import PreferencesContainer from "../../features/settings/containers/Preferences";
+import CategoriesContainer from "../../features/settings/containers/Categories";
+import CategoryContainer from "../../features/settings/containers/Category";
+import TaxesContainer from "../../features/settings/containers/Taxes";
+import TaxContainer from "../../features/settings/containers/Tax";
+import EndpointContainer from "../../features/authentication/containers/Endpoint";
+
+export const SettingNavigator = {
+
+ // Settings
+ // -----------------------------------------
+ [ROUTES.SETTING_LIST]: generateStackNavigation(
+ ROUTES.SETTING_LIST,
+ SettingsContainer,
+ ),
+ [ROUTES.LANGUAGE_AND_CURRENCY]: generateStackNavigation(
+ ROUTES.LANGUAGE_AND_CURRENCY,
+ LanguageAndCurrencyContainer,
+ ),
+ [ROUTES.NOTIFICATIONS]: generateStackNavigation(
+ ROUTES.NOTIFICATIONS,
+ NotificationContainer,
+ ),
+ [ROUTES.PREFERENCES]: generateStackNavigation(
+ ROUTES.PREFERENCES,
+ PreferencesContainer,
+ ),
+
+ // User Information
+ // -----------------------------------------
+ [ROUTES.ACCOUNT_INFO]: generateStackNavigation(
+ ROUTES.ACCOUNT_INFO,
+ AccountContainer,
+ ),
+ [ROUTES.COMPANY_INFO]: generateStackNavigation(
+ ROUTES.COMPANY_INFO,
+ CompanyContainer,
+ ),
+
+ // Taxes
+ // -----------------------------------------
+ [ROUTES.TAXES]: generateStackNavigation(
+ ROUTES.TAXES,
+ TaxesContainer,
+ ),
+ [ROUTES.TAX]: generateStackNavigation(
+ ROUTES.TAX,
+ TaxContainer,
+ ),
+
+ // Categories
+ // -----------------------------------------
+ [ROUTES.CATEGORIES]: generateStackNavigation(
+ ROUTES.CATEGORIES,
+ CategoriesContainer,
+ ),
+ [ROUTES.CATEGORY]: generateStackNavigation(
+ ROUTES.CATEGORY,
+ CategoryContainer,
+ ),
+ [ROUTES.ENDPOINTS_SETTINGS]: generateStackNavigation(
+ ROUTES.ENDPOINTS_SETTINGS,
+ EndpointContainer
+ ),
+
+}
diff --git a/src/navigation/reducers/index.js b/src/navigation/reducers/index.js
new file mode 100644
index 00000000..7a0140f4
--- /dev/null
+++ b/src/navigation/reducers/index.js
@@ -0,0 +1,15 @@
+import ApplicationNavigator from "../navigators";
+import { ROUTES } from "../routes";
+
+const navigationData = (state, action) => {
+ const nextState = ApplicationNavigator.router.getStateForAction(
+ action,
+ state
+ );
+
+ return nextState || state;
+};
+
+export default navigationData;
+
+
diff --git a/src/navigation/routes.js b/src/navigation/routes.js
new file mode 100644
index 00000000..a95be5aa
--- /dev/null
+++ b/src/navigation/routes.js
@@ -0,0 +1,86 @@
+export const ROUTES = {
+ AUTH: 'AUTH',
+ LOGIN: 'LOGIN',
+ FORGOT_PASSWORD: 'FORGOT_PASSWORD',
+ ENDPOINTS: 'ENDPOINTS',
+
+ // Main Bottom Tabs
+ // -----------------------------------------
+ MAIN_TABS: 'MAIN_TABS',
+ MAIN_INVOICES: 'MAIN_INVOICES',
+ MAIN_CUSTOMERS: 'MAIN_CUSTOMERS',
+ MAIN_PAYMENTS: 'MAIN_PAYMENTS',
+ MAIN_EXPENSES: 'MAIN_EXPENSES',
+ MAIN_MORE: 'MAIN_MORE',
+ MAIN_DASHBOARD: 'MAIN_DASHBOARD',
+ MAIN_SETTINGS: 'MAIN_SETTINGS',
+
+ // Invoice & items
+ // -----------------------------------------
+ INVOICE: 'INVOICE',
+ INVOICE_ITEM: 'INVOICE_ITEM',
+
+ // Items
+ // -----------------------------------------
+ ADD_ITEMS: 'ADD_ITEMS',
+ EDIT_ITEM: 'EDIT_ITEM',
+ GLOBAL_ITEM: 'GLOBAL_ITEM',
+ GLOBAL_ITEMS: 'GLOBAL_ITEMS',
+
+ // Estimate & items
+ // -----------------------------------------
+ ESTIMATE_LIST: 'ESTIMATE_LIST',
+ ESTIMATE: 'ESTIMATE',
+ ESTIMATE_ITEM: 'ESTIMATE_ITEM',
+
+ // Customer
+ // -----------------------------------------
+ CUSTOMER_FIELD: 'CUSTOMER_FIELD',
+ CUSTOMER: 'CUSTOMER',
+
+ // More
+ // -----------------------------------------
+ MORE: 'MORE',
+
+ // Categories
+ // -----------------------------------------
+ CATEGORIES: 'CATEGORIES',
+ CATEGORY: 'CATEGORY',
+
+ // Taxes
+ // -----------------------------------------
+ TAX: 'TAX',
+ TAXES: 'TAXES',
+
+ // Settings
+ // -----------------------------------------
+ SETTING: 'SETTING',
+ SETTING_LIST: 'SETTING_LIST',
+ ACCOUNT_INFO: 'ACCOUNT_INFO',
+ COMPANY_INFO: 'COMPANY_INFO',
+ NOTIFICATIONS: 'NOTIFICATIONS',
+ PREFERENCES: 'PREFERENCES',
+ LANGUAGE_AND_CURRENCY: 'LANGUAGE_AND_CURRENCY',
+ ENDPOINTS_SETTINGS: 'ENDPOINTS_SETTINGS',
+
+ // Expenses
+ // -----------------------------------------
+ ADD_EXPENSE: 'ADD_EXPENSE',
+ EXPENSE: 'EXPENSE',
+
+ // Payments
+ // -----------------------------------------
+ PAYMENT: 'PAYMENT',
+
+ // Report
+ // -----------------------------------------
+ REPORTS: 'REPORTS',
+ GENERATE_REPORT: 'GENERATE_REPORT',
+
+ // Lost Connection
+ LOST_CONNECTION: 'LOST_CONNECTION',
+
+ // Update App Version
+ UPDATE_APP_VERSION: 'UPDATE_APP_VERSION',
+
+};
diff --git a/src/reducers/global.js b/src/reducers/global.js
new file mode 100644
index 00000000..6ef10e94
--- /dev/null
+++ b/src/reducers/global.js
@@ -0,0 +1,159 @@
+import {
+ SET_GLOBAL_BOOTSTRAP,
+ SET_SETTINGS,
+ GLOBAL_TRIGGER_SPINNER,
+ DATE_FORMAT,
+ SAVE_ENDPOINT_API,
+ SET_APP_VERSION,
+} from "../api/consts";
+import { SET_TAX, SET_EDIT_TAX, SET_REMOVE_TAX, SET_TAXES } from "../features/settings/constants";
+import { formatTaxTypes } from "../api/global";
+
+const initialState = {
+ customers: [],
+ currencies: [],
+ language: 'en',
+ timeZone: null,
+ discountPerItem: false,
+ taxPerItem: false,
+ notifyInvoiceViewed: false,
+ notifyEstimateViewed: false,
+ currency: null,
+ company: null,
+ taxTypes: [],
+ loading: false,
+ dateFormat: DATE_FORMAT,
+ endpointApi: null,
+ endpointURL: null,
+ fiscalYear: '2-1',
+ appVersion: '1.0.0'
+};
+
+export default function globalReducer(state = initialState, action) {
+ const { payload, type } = action;
+
+ switch (type) {
+ case GLOBAL_TRIGGER_SPINNER:
+ const { appLoginLoading } = payload
+
+ return { ...state, loading: appLoginLoading }
+
+ case SAVE_ENDPOINT_API:
+
+ const { endpointURL = '' } = payload
+
+ return {
+ ...state,
+ endpointURL,
+ endpointApi: endpointURL ? `${endpointURL}/api/` : null
+ };
+
+ case SET_APP_VERSION:
+
+ const { app_version } = payload
+ return { ...state, appVersion: app_version }
+
+ case SET_GLOBAL_BOOTSTRAP:
+
+ const {
+ currencies,
+ customers,
+ default_currency,
+ company,
+ taxTypes,
+ moment_date_format,
+ fiscal_year,
+ default_language = 'en'
+ } = payload
+
+ const taxList = formatTaxTypes(taxTypes)
+
+ return {
+ ...state,
+ currencies,
+ customers,
+ currency: default_currency,
+ company,
+ dateFormat: moment_date_format,
+ taxTypes: taxList,
+ fiscalYear: fiscal_year,
+ language: default_language
+ };
+
+ case SET_TAXES:
+
+ const taxes = formatTaxTypes(payload.taxTypes)
+
+ return { ...state, taxTypes: taxes };
+
+ case SET_TAX:
+
+ const tax = formatTaxTypes(payload.taxType)
+
+ return {
+ ...state,
+ taxTypes: [...tax, ...state.taxTypes]
+ };
+
+ case SET_EDIT_TAX:
+
+ let editTax = formatTaxTypes(payload.taxType)
+ const taxTypeList = state.taxTypes.filter(({ fullItem }) =>
+ (fullItem.id !== payload.taxId))
+
+ return {
+ ...state,
+ taxTypes: [...editTax, ...taxTypeList]
+ };
+
+ case SET_REMOVE_TAX:
+
+ const remainTaxes = state.taxTypes.filter(({ fullItem }) =>
+ (fullItem.id !== payload.taxId))
+
+ return { ...state, taxTypes: remainTaxes };
+
+ case SET_SETTINGS:
+
+ let { key, value } = payload.settings
+
+ if (key) {
+ if (key === 'discount_per_item') {
+ return {
+ ...state,
+ discountPerItem: value === 'YES' ? true : false
+ };
+ }
+ if (key === 'tax_per_item') {
+ return {
+ ...state,
+ taxPerItem: value === 'YES' ? true : false
+ };
+ }
+ if (key === 'notify_invoice_viewed') {
+ return {
+ ...state,
+ notifyInvoiceViewed: value === 'YES' ? true : false
+ };
+ }
+ if (key === 'notify_estimate_viewed') {
+ return {
+ ...state,
+ notifyEstimateViewed: value === 'YES' ? true : false
+ };
+ }
+ }
+ else
+ return {
+ ...state,
+ language: payload.settings.language,
+ timeZone: payload.settings.time_zone,
+ dateFormat: payload.settings.moment_date_format,
+ fiscalYear: payload.settings.fiscal_year,
+ currency: payload.currency,
+ };
+
+ default:
+ return state;
+ }
+}
diff --git a/src/reducers/index.js b/src/reducers/index.js
new file mode 100644
index 00000000..3ef4e4bf
--- /dev/null
+++ b/src/reducers/index.js
@@ -0,0 +1,30 @@
+import { combineReducers } from "redux";
+import auth from '../features/authentication/reducers';
+import invoices from '../features/invoices/reducers';
+import estimates from '../features/estimates/reducers';
+import customers from '../features/customers/reducers';
+import payments from '../features/payments/reducers';
+import more from '../features/more/reducers';
+import settings from '../features/settings/reducers';
+import expenses from '../features/expenses/reducers';
+import global from './global';
+import navigationData from "../navigation/reducers";
+import { reducer as formReducer } from 'redux-form';
+import Navigator from "../navigation/navigators";
+import { createNavigationReducer } from 'react-navigation-redux-helpers';
+
+
+export default combineReducers({
+ auth,
+ invoices,
+ estimates,
+ customers,
+ more,
+ expenses,
+ payments,
+ navigationData,
+ form: formReducer,
+ global,
+ settings,
+ nav: createNavigationReducer(Navigator),
+});
diff --git a/src/saga/index.js b/src/saga/index.js
new file mode 100644
index 00000000..6eb884d8
--- /dev/null
+++ b/src/saga/index.js
@@ -0,0 +1,46 @@
+import { all, takeEvery, select, put } from 'redux-saga/effects';
+import { REHYDRATE } from 'redux-persist/src/constants';
+import { NavigationActions } from 'react-navigation';
+import auth from '../features/authentication/saga';
+import invoices from '../features/invoices/saga';
+import estimates from '../features/estimates/saga';
+import customers from '../features/customers/saga';
+import expenses from '../features/expenses/saga';
+import payments from '../features/payments/saga';
+import settings from '../features/settings/saga';
+import more from '../features/more/saga';
+import { ROUTES } from '../navigation/routes';
+import { store } from '../store';
+import moment from 'moment';
+
+export default function* rootSaga() {
+ yield takeEvery(REHYDRATE, function* boot() {
+ const { routes } = yield select((state) => state.nav);
+ const currentRoteBlock = routes[routes.length - 1];
+ const currentRouteBlockName = currentRoteBlock.routeName;
+
+ const reduxStore = store.getState();
+
+ if (currentRouteBlockName !== ROUTES.AUTH) {
+ yield put(NavigationActions.navigate({ routeName: ROUTES.MAIN_INVOICES }));
+ } else {
+ const { endpointApi, endpointURL } = reduxStore.global
+
+ if (!endpointApi || !endpointURL) {
+ yield put(NavigationActions.navigate({ routeName: ROUTES.ENDPOINTS }));
+ }
+
+ }
+
+ yield all([
+ auth(),
+ invoices(),
+ estimates(),
+ customers(),
+ more(),
+ expenses(),
+ payments(),
+ settings(),
+ ]);
+ });
+}
diff --git a/src/store.js b/src/store.js
new file mode 100644
index 00000000..86f14f3c
--- /dev/null
+++ b/src/store.js
@@ -0,0 +1,26 @@
+import { applyMiddleware, createStore, compose } from "redux";
+import rootReducer from "./reducers";
+import * as reduxStorage from 'redux-storage';
+import createSagaMiddleware from 'redux-saga';
+import sagas from './saga';
+import { persistStore, persistReducer } from 'redux-persist';
+import { AsyncStorage } from 'react-native';
+
+const persistConfig = {
+ key: 'root',
+ storage: AsyncStorage,
+ whitelist: ['auth', 'nav', 'settings', 'global', 'more'],
+ blackList: ['form'],
+};
+
+const reducer = reduxStorage.reducer(rootReducer);
+
+const persistedReducer = persistReducer(persistConfig, reducer);
+
+const sagaMiddleware = createSagaMiddleware();
+
+export const store = createStore(persistedReducer, applyMiddleware(sagaMiddleware));
+
+export const persistor = persistStore(store);
+
+sagaMiddleware.run(sagas);
diff --git a/src/styles/colors.js b/src/styles/colors.js
new file mode 100644
index 00000000..deeb4e36
--- /dev/null
+++ b/src/styles/colors.js
@@ -0,0 +1,35 @@
+export const colors = {
+ primary: '#5851D8',
+ primaryLight: '#817AE3',
+ primaryLight2: 'rgba(88, 81, 216, 0.2)',
+ secondary: '#55547A',
+ secondaryDark: '#595D6E',
+ success: '#00C99C',
+ successLight: '#D4F6EE',
+ successLight2: '#D5EED0',
+ successDark: '#354930',
+ info: '#15B2EC',
+ infoLight: '#C9E3EC',
+ infoDark: '#1E576C',
+ danger: '#FB7178',
+ dangerLight: '#FED7D7',
+ dangerDark: '#9B2C2C',
+ warning: '#F3AF4E',
+ warningLight: '#F8EDCB',
+ warningLight2: 'rgba(246, 208, 154, 0.4)',
+ warningDark: '#6c432e',
+ warningDark2: '#A96E1A',
+ dark: '#040405',
+ dark2: '#263B5E',
+ dark3: '#555577',
+ veryLightGray: '#F9FBFF',
+ lightGray: '#EAF1FB',
+ gray: '#B9C1D1',
+ gray2: '#E1E0EA',
+ darkGray: '#A5ACC1',
+ darkGray2: '#312F57',
+ veryDarkGray: '#595959',
+ pink: '#FA6FD1',
+ lightGreen: '#daf5ee',
+ white: '#FFFFFF',
+};
diff --git a/src/styles/fonts.js b/src/styles/fonts.js
new file mode 100644
index 00000000..1cc98f08
--- /dev/null
+++ b/src/styles/fonts.js
@@ -0,0 +1,7 @@
+export const fonts = {
+ poppins: 'Poppins',
+ poppinsLight: 'Poppins-light',
+ poppinsMedium: 'Poppins-medium',
+ poppinsSemiBold: 'Poppins-semi-bold',
+ poppinsBold: 'Poppins-bold',
+};
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 00000000..700583cd
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,6343 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
+ integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==
+ dependencies:
+ "@babel/highlight" "^7.0.0"
+
+"@babel/core@^7.0.0", "@babel/core@^7.1.0":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.5.tgz#17b2686ef0d6bc58f963dddd68ab669755582c30"
+ integrity sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==
+ dependencies:
+ "@babel/code-frame" "^7.5.5"
+ "@babel/generator" "^7.5.5"
+ "@babel/helpers" "^7.5.5"
+ "@babel/parser" "^7.5.5"
+ "@babel/template" "^7.4.4"
+ "@babel/traverse" "^7.5.5"
+ "@babel/types" "^7.5.5"
+ convert-source-map "^1.1.0"
+ debug "^4.1.0"
+ json5 "^2.1.0"
+ lodash "^4.17.13"
+ resolve "^1.3.2"
+ semver "^5.4.1"
+ source-map "^0.5.0"
+
+"@babel/generator@^7.0.0", "@babel/generator@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf"
+ integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==
+ dependencies:
+ "@babel/types" "^7.5.5"
+ jsesc "^2.5.1"
+ lodash "^4.17.13"
+ source-map "^0.5.0"
+ trim-right "^1.0.1"
+
+"@babel/helper-annotate-as-pure@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32"
+ integrity sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz#6b69628dfe4087798e0c4ed98e3d4a6b2fbd2f5f"
+ integrity sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==
+ dependencies:
+ "@babel/helper-explode-assignable-expression" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-builder-react-jsx@^7.3.0":
+ version "7.3.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.3.0.tgz#a1ac95a5d2b3e88ae5e54846bf462eeb81b318a4"
+ integrity sha512-MjA9KgwCuPEkQd9ncSXvSyJ5y+j2sICHyrI0M3L+6fnS4wMSNDc1ARXsbTfbb2cXHn17VisSnU/sHFTCxVxSMw==
+ dependencies:
+ "@babel/types" "^7.3.0"
+ esutils "^2.0.0"
+
+"@babel/helper-call-delegate@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz#87c1f8ca19ad552a736a7a27b1c1fcf8b1ff1f43"
+ integrity sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==
+ dependencies:
+ "@babel/helper-hoist-variables" "^7.4.4"
+ "@babel/traverse" "^7.4.4"
+ "@babel/types" "^7.4.4"
+
+"@babel/helper-create-class-features-plugin@^7.4.4", "@babel/helper-create-class-features-plugin@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.5.tgz#401f302c8ddbc0edd36f7c6b2887d8fa1122e5a4"
+ integrity sha512-ZsxkyYiRA7Bg+ZTRpPvB6AbOFKTFFK4LrvTet8lInm0V468MWCaSYJE+I7v2z2r8KNLtYiV+K5kTCnR7dvyZjg==
+ dependencies:
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/helper-member-expression-to-functions" "^7.5.5"
+ "@babel/helper-optimise-call-expression" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-replace-supers" "^7.5.5"
+ "@babel/helper-split-export-declaration" "^7.4.4"
+
+"@babel/helper-define-map@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz#3dec32c2046f37e09b28c93eb0b103fd2a25d369"
+ integrity sha512-fTfxx7i0B5NJqvUOBBGREnrqbTxRh7zinBANpZXAVDlsZxYdclDp467G1sQ8VZYMnAURY3RpBUAgOYT9GfzHBg==
+ dependencies:
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/types" "^7.5.5"
+ lodash "^4.17.13"
+
+"@babel/helper-explode-assignable-expression@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz#537fa13f6f1674df745b0c00ec8fe4e99681c8f6"
+ integrity sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==
+ dependencies:
+ "@babel/traverse" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-function-name@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53"
+ integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==
+ dependencies:
+ "@babel/helper-get-function-arity" "^7.0.0"
+ "@babel/template" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-get-function-arity@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3"
+ integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-hoist-variables@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz#0298b5f25c8c09c53102d52ac4a98f773eb2850a"
+ integrity sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==
+ dependencies:
+ "@babel/types" "^7.4.4"
+
+"@babel/helper-member-expression-to-functions@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz#1fb5b8ec4453a93c439ee9fe3aeea4a84b76b590"
+ integrity sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA==
+ dependencies:
+ "@babel/types" "^7.5.5"
+
+"@babel/helper-module-imports@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d"
+ integrity sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-module-transforms@^7.1.0", "@babel/helper-module-transforms@^7.4.4":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz#f84ff8a09038dcbca1fd4355661a500937165b4a"
+ integrity sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw==
+ dependencies:
+ "@babel/helper-module-imports" "^7.0.0"
+ "@babel/helper-simple-access" "^7.1.0"
+ "@babel/helper-split-export-declaration" "^7.4.4"
+ "@babel/template" "^7.4.4"
+ "@babel/types" "^7.5.5"
+ lodash "^4.17.13"
+
+"@babel/helper-optimise-call-expression@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz#a2920c5702b073c15de51106200aa8cad20497d5"
+ integrity sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-plugin-utils@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250"
+ integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==
+
+"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351"
+ integrity sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==
+ dependencies:
+ lodash "^4.17.13"
+
+"@babel/helper-remap-async-to-generator@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz#361d80821b6f38da75bd3f0785ece20a88c5fe7f"
+ integrity sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.0.0"
+ "@babel/helper-wrap-function" "^7.1.0"
+ "@babel/template" "^7.1.0"
+ "@babel/traverse" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-replace-supers@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz#f84ce43df031222d2bad068d2626cb5799c34bc2"
+ integrity sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg==
+ dependencies:
+ "@babel/helper-member-expression-to-functions" "^7.5.5"
+ "@babel/helper-optimise-call-expression" "^7.0.0"
+ "@babel/traverse" "^7.5.5"
+ "@babel/types" "^7.5.5"
+
+"@babel/helper-simple-access@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz#65eeb954c8c245beaa4e859da6188f39d71e585c"
+ integrity sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==
+ dependencies:
+ "@babel/template" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@babel/helper-split-export-declaration@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677"
+ integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==
+ dependencies:
+ "@babel/types" "^7.4.4"
+
+"@babel/helper-wrap-function@^7.1.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa"
+ integrity sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==
+ dependencies:
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/template" "^7.1.0"
+ "@babel/traverse" "^7.1.0"
+ "@babel/types" "^7.2.0"
+
+"@babel/helpers@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.5.tgz#63908d2a73942229d1e6685bc2a0e730dde3b75e"
+ integrity sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g==
+ dependencies:
+ "@babel/template" "^7.4.4"
+ "@babel/traverse" "^7.5.5"
+ "@babel/types" "^7.5.5"
+
+"@babel/highlight@^7.0.0":
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540"
+ integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==
+ dependencies:
+ chalk "^2.0.0"
+ esutils "^2.0.2"
+ js-tokens "^4.0.0"
+
+"@babel/parser@^7.0.0", "@babel/parser@^7.4.4", "@babel/parser@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b"
+ integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==
+
+"@babel/plugin-external-helpers@^7.0.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-external-helpers/-/plugin-external-helpers-7.2.0.tgz#7f4cb7dee651cd380d2034847d914288467a6be4"
+ integrity sha512-QFmtcCShFkyAsNtdCM3lJPmRe1iB+vPZymlB4LnDIKEBj2yKQLQKtoxXxJ8ePT5fwMl4QGg303p4mB0UsSI2/g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-proposal-async-generator-functions@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e"
+ integrity sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-remap-async-to-generator" "^7.1.0"
+ "@babel/plugin-syntax-async-generators" "^7.2.0"
+
+"@babel/plugin-proposal-class-properties@^7.0.0":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz#a974cfae1e37c3110e71f3c6a2e48b8e71958cd4"
+ integrity sha512-AF79FsnWFxjlaosgdi421vmYG6/jg79bVD0dpD44QdgobzHKuLZ6S3vl8la9qIeSwGi8i1fS0O1mfuDAAdo1/A==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.5.5"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-proposal-decorators@^7.1.0":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.4.tgz#de9b2a1a8ab0196f378e2a82f10b6e2a36f21cc0"
+ integrity sha512-z7MpQz3XC/iQJWXH9y+MaWcLPNSMY9RQSthrLzak8R8hCj0fuyNk+Dzi9kfNe/JxxlWQ2g7wkABbgWjW36MTcw==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.4.4"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-decorators" "^7.2.0"
+
+"@babel/plugin-proposal-dynamic-import@^7.5.0":
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz#e532202db4838723691b10a67b8ce509e397c506"
+ integrity sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-dynamic-import" "^7.2.0"
+
+"@babel/plugin-proposal-export-default-from@^7.0.0":
+ version "7.5.2"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.5.2.tgz#2c0ac2dcc36e3b2443fead2c3c5fc796fb1b5145"
+ integrity sha512-wr9Itk05L1/wyyZKVEmXWCdcsp/e185WUNl6AfYZeEKYaUPPvHXRDqO5K1VH7/UamYqGJowFRuCv30aDYZawsg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-export-default-from" "^7.2.0"
+
+"@babel/plugin-proposal-json-strings@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317"
+ integrity sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-json-strings" "^7.2.0"
+
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.4.4.tgz#41c360d59481d88e0ce3a3f837df10121a769b39"
+ integrity sha512-Amph7Epui1Dh/xxUxS2+K22/MUi6+6JVTvy3P58tja3B6yKTSjwwx0/d83rF7551D6PVSSoplQb8GCwqec7HRw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.2.0"
+
+"@babel/plugin-proposal-object-rest-spread@^7.0.0", "@babel/plugin-proposal-object-rest-spread@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58"
+ integrity sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-object-rest-spread" "^7.2.0"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.0.0", "@babel/plugin-proposal-optional-catch-binding@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5"
+ integrity sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
+
+"@babel/plugin-proposal-optional-chaining@^7.0.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.2.0.tgz#ae454f4c21c6c2ce8cb2397dc332ae8b420c5441"
+ integrity sha512-ea3Q6edZC/55wEBVZAEz42v528VulyO0eir+7uky/sT4XRcdkWJcFi1aPtitTlwUzGnECWJNExWww1SStt+yWw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-optional-chaining" "^7.2.0"
+
+"@babel/plugin-proposal-unicode-property-regex@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78"
+ integrity sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-regex" "^7.4.4"
+ regexpu-core "^4.5.4"
+
+"@babel/plugin-syntax-async-generators@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz#69e1f0db34c6f5a0cf7e2b3323bf159a76c8cb7f"
+ integrity sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-class-properties@^7.0.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.2.0.tgz#23b3b7b9bcdabd73672a9149f728cd3be6214812"
+ integrity sha512-UxYaGXYQ7rrKJS/PxIKRkv3exi05oH7rokBAsmCSsCxz1sVPZ7Fu6FzKoGgUvmY+0YgSkYHgUoCh5R5bCNBQlw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-decorators@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz#c50b1b957dcc69e4b1127b65e1c33eef61570c1b"
+ integrity sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-dynamic-import@^7.0.0", "@babel/plugin-syntax-dynamic-import@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz#69c159ffaf4998122161ad8ebc5e6d1f55df8612"
+ integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.2.0.tgz#edd83b7adc2e0d059e2467ca96c650ab6d2f3820"
+ integrity sha512-c7nqUnNST97BWPtoe+Ssi+fJukc9P9/JMZ71IOMNQWza2E+Psrd46N6AEvtw6pqK+gt7ChjXyrw4SPDO79f3Lw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.2.0.tgz#a765f061f803bc48f240c26f8747faf97c26bf7c"
+ integrity sha512-r6YMuZDWLtLlu0kqIim5o/3TNRAlWb073HwT3e2nKf9I8IIvOggPrnILYPsrrKilmn/mYEMCf/Z07w3yQJF6dg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-json-strings@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470"
+ integrity sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz#0b85a3b4bc7cdf4cc4b8bf236335b907ca22e7c7"
+ integrity sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.2.0.tgz#f75083dfd5ade73e783db729bbd87e7b9efb7624"
+ integrity sha512-lRCEaKE+LTxDQtgbYajI04ddt6WW0WJq57xqkAZ+s11h4YgfRHhVA/Y2VhfPzzFD4qeLHWg32DMp9HooY4Kqlg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e"
+ integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c"
+ integrity sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-optional-chaining@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.2.0.tgz#a59d6ae8c167e7608eaa443fda9fa8fa6bf21dff"
+ integrity sha512-HtGCtvp5Uq/jH/WNUPkK6b7rufnCPLLlDAFN7cmACoIjaOOiXxUt3SswU5loHqrhtqTsa/WoLQ1OQ1AGuZqaWA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-syntax-typescript@^7.2.0":
+ version "7.3.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz#a7cc3f66119a9f7ebe2de5383cce193473d65991"
+ integrity sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-arrow-functions@^7.0.0", "@babel/plugin-transform-arrow-functions@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550"
+ integrity sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-async-to-generator@^7.0.0", "@babel/plugin-transform-async-to-generator@^7.5.0":
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e"
+ integrity sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg==
+ dependencies:
+ "@babel/helper-module-imports" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-remap-async-to-generator" "^7.1.0"
+
+"@babel/plugin-transform-block-scoped-functions@^7.0.0", "@babel/plugin-transform-block-scoped-functions@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190"
+ integrity sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-block-scoping@^7.0.0", "@babel/plugin-transform-block-scoping@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.5.5.tgz#a35f395e5402822f10d2119f6f8e045e3639a2ce"
+ integrity sha512-82A3CLRRdYubkG85lKwhZB0WZoHxLGsJdux/cOVaJCJpvYFl1LVzAIFyRsa7CvXqW8rBM4Zf3Bfn8PHt5DP0Sg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ lodash "^4.17.13"
+
+"@babel/plugin-transform-classes@^7.0.0", "@babel/plugin-transform-classes@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz#d094299d9bd680a14a2a0edae38305ad60fb4de9"
+ integrity sha512-U2htCNK/6e9K7jGyJ++1p5XRU+LJjrwtoiVn9SzRlDT2KubcZ11OOwy3s24TjHxPgxNwonCYP7U2K51uVYCMDg==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.0.0"
+ "@babel/helper-define-map" "^7.5.5"
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/helper-optimise-call-expression" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-replace-supers" "^7.5.5"
+ "@babel/helper-split-export-declaration" "^7.4.4"
+ globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.0.0", "@babel/plugin-transform-computed-properties@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da"
+ integrity sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-destructuring@^7.0.0", "@babel/plugin-transform-destructuring@^7.5.0":
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz#f6c09fdfe3f94516ff074fe877db7bc9ef05855a"
+ integrity sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-dotall-regex@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3"
+ integrity sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-regex" "^7.4.4"
+ regexpu-core "^4.5.4"
+
+"@babel/plugin-transform-duplicate-keys@^7.5.0":
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz#c5dbf5106bf84cdf691222c0974c12b1df931853"
+ integrity sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-exponentiation-operator@^7.0.0", "@babel/plugin-transform-exponentiation-operator@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008"
+ integrity sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==
+ dependencies:
+ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-flow-strip-types@^7.0.0":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.4.4.tgz#d267a081f49a8705fc9146de0768c6b58dccd8f7"
+ integrity sha512-WyVedfeEIILYEaWGAUWzVNyqG4sfsNooMhXWsu/YzOvVGcsnPb5PguysjJqI3t3qiaYj0BR8T2f5njdjTGe44Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-flow" "^7.2.0"
+
+"@babel/plugin-transform-for-of@^7.0.0", "@babel/plugin-transform-for-of@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556"
+ integrity sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-function-name@^7.0.0", "@babel/plugin-transform-function-name@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz#e1436116abb0610c2259094848754ac5230922ad"
+ integrity sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==
+ dependencies:
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-literals@^7.0.0", "@babel/plugin-transform-literals@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1"
+ integrity sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-member-expression-literals@^7.0.0", "@babel/plugin-transform-member-expression-literals@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz#fa10aa5c58a2cb6afcf2c9ffa8cb4d8b3d489a2d"
+ integrity sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-modules-amd@^7.5.0":
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz#ef00435d46da0a5961aa728a1d2ecff063e4fb91"
+ integrity sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.1.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.4.4", "@babel/plugin-transform-modules-commonjs@^7.5.0":
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74"
+ integrity sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.4.4"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-simple-access" "^7.1.0"
+ babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-systemjs@^7.5.0":
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz#e75266a13ef94202db2a0620977756f51d52d249"
+ integrity sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg==
+ dependencies:
+ "@babel/helper-hoist-variables" "^7.4.4"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-umd@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz#7678ce75169f0877b8eb2235538c074268dd01ae"
+ integrity sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.1.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.4.5":
+ version "7.4.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz#9d269fd28a370258199b4294736813a60bbdd106"
+ integrity sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg==
+ dependencies:
+ regexp-tree "^0.1.6"
+
+"@babel/plugin-transform-new-target@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz#18d120438b0cc9ee95a47f2c72bc9768fbed60a5"
+ integrity sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-object-assign@^7.0.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.2.0.tgz#6fdeea42be17040f119e38e23ea0f49f31968bde"
+ integrity sha512-nmE55cZBPFgUktbF2OuoZgPRadfxosLOpSgzEPYotKSls9J4pEPcembi8r78RU37Rph6UApCpNmsQA4QMWK9Ng==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-object-super@^7.0.0", "@babel/plugin-transform-object-super@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz#c70021df834073c65eb613b8679cc4a381d1a9f9"
+ integrity sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-replace-supers" "^7.5.5"
+
+"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16"
+ integrity sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==
+ dependencies:
+ "@babel/helper-call-delegate" "^7.4.4"
+ "@babel/helper-get-function-arity" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-property-literals@^7.0.0", "@babel/plugin-transform-property-literals@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz#03e33f653f5b25c4eb572c98b9485055b389e905"
+ integrity sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-react-display-name@^7.0.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz#ebfaed87834ce8dc4279609a4f0c324c156e3eb0"
+ integrity sha512-Htf/tPa5haZvRMiNSQSFifK12gtr/8vwfr+A9y69uF0QcU77AVu4K7MiHEkTxF7lQoHOL0F9ErqgfNEAKgXj7A==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-react-jsx-source@^7.0.0":
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.5.0.tgz#583b10c49cf057e237085bcbd8cc960bd83bd96b"
+ integrity sha512-58Q+Jsy4IDCZx7kqEZuSDdam/1oW8OdDX8f+Loo6xyxdfg1yF0GE2XNJQSTZCaMol93+FBzpWiPEwtbMloAcPg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-jsx" "^7.2.0"
+
+"@babel/plugin-transform-react-jsx@^7.0.0":
+ version "7.3.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.3.0.tgz#f2cab99026631c767e2745a5368b331cfe8f5290"
+ integrity sha512-a/+aRb7R06WcKvQLOu4/TpjKOdvVEKRLWFpKcNuHhiREPgGRB4TQJxq07+EZLS8LFVYpfq1a5lDUnuMdcCpBKg==
+ dependencies:
+ "@babel/helper-builder-react-jsx" "^7.3.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-jsx" "^7.2.0"
+
+"@babel/plugin-transform-regenerator@^7.0.0", "@babel/plugin-transform-regenerator@^7.4.5":
+ version "7.4.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz#629dc82512c55cee01341fb27bdfcb210354680f"
+ integrity sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==
+ dependencies:
+ regenerator-transform "^0.14.0"
+
+"@babel/plugin-transform-reserved-words@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz#4792af87c998a49367597d07fedf02636d2e1634"
+ integrity sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-runtime@^7.0.0":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.5.tgz#a6331afbfc59189d2135b2e09474457a8e3d28bc"
+ integrity sha512-6Xmeidsun5rkwnGfMOp6/z9nSzWpHFNVr2Jx7kwoq4mVatQfQx5S56drBgEHF+XQbKOdIaOiMIINvp/kAwMN+w==
+ dependencies:
+ "@babel/helper-module-imports" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ resolve "^1.8.1"
+ semver "^5.5.1"
+
+"@babel/plugin-transform-shorthand-properties@^7.0.0", "@babel/plugin-transform-shorthand-properties@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0"
+ integrity sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-spread@^7.0.0", "@babel/plugin-transform-spread@^7.2.0":
+ version "7.2.2"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz#3103a9abe22f742b6d406ecd3cd49b774919b406"
+ integrity sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-sticky-regex@^7.0.0", "@babel/plugin-transform-sticky-regex@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1"
+ integrity sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-regex" "^7.0.0"
+
+"@babel/plugin-transform-template-literals@^7.0.0", "@babel/plugin-transform-template-literals@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0"
+ integrity sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-typeof-symbol@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz#117d2bcec2fbf64b4b59d1f9819894682d29f2b2"
+ integrity sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-typescript@^7.0.0":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.5.5.tgz#6d862766f09b2da1cb1f7d505fe2aedab6b7d4b8"
+ integrity sha512-pehKf4m640myZu5B2ZviLaiBlxMCjSZ1qTEO459AXKX5GnPueyulJeCqZFs1nz/Ya2dDzXQ1NxZ/kKNWyD4h6w==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.5.5"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-syntax-typescript" "^7.2.0"
+
+"@babel/plugin-transform-unicode-regex@^7.0.0", "@babel/plugin-transform-unicode-regex@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz#ab4634bb4f14d36728bf5978322b35587787970f"
+ integrity sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/helper-regex" "^7.4.4"
+ regexpu-core "^4.5.4"
+
+"@babel/preset-env@^7.3.1":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.5.tgz#bc470b53acaa48df4b8db24a570d6da1fef53c9a"
+ integrity sha512-GMZQka/+INwsMz1A5UEql8tG015h5j/qjptpKY2gJ7giy8ohzU710YciJB5rcKsWGWHiW3RUnHib0E5/m3Tp3A==
+ dependencies:
+ "@babel/helper-module-imports" "^7.0.0"
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-proposal-async-generator-functions" "^7.2.0"
+ "@babel/plugin-proposal-dynamic-import" "^7.5.0"
+ "@babel/plugin-proposal-json-strings" "^7.2.0"
+ "@babel/plugin-proposal-object-rest-spread" "^7.5.5"
+ "@babel/plugin-proposal-optional-catch-binding" "^7.2.0"
+ "@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
+ "@babel/plugin-syntax-async-generators" "^7.2.0"
+ "@babel/plugin-syntax-dynamic-import" "^7.2.0"
+ "@babel/plugin-syntax-json-strings" "^7.2.0"
+ "@babel/plugin-syntax-object-rest-spread" "^7.2.0"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
+ "@babel/plugin-transform-arrow-functions" "^7.2.0"
+ "@babel/plugin-transform-async-to-generator" "^7.5.0"
+ "@babel/plugin-transform-block-scoped-functions" "^7.2.0"
+ "@babel/plugin-transform-block-scoping" "^7.5.5"
+ "@babel/plugin-transform-classes" "^7.5.5"
+ "@babel/plugin-transform-computed-properties" "^7.2.0"
+ "@babel/plugin-transform-destructuring" "^7.5.0"
+ "@babel/plugin-transform-dotall-regex" "^7.4.4"
+ "@babel/plugin-transform-duplicate-keys" "^7.5.0"
+ "@babel/plugin-transform-exponentiation-operator" "^7.2.0"
+ "@babel/plugin-transform-for-of" "^7.4.4"
+ "@babel/plugin-transform-function-name" "^7.4.4"
+ "@babel/plugin-transform-literals" "^7.2.0"
+ "@babel/plugin-transform-member-expression-literals" "^7.2.0"
+ "@babel/plugin-transform-modules-amd" "^7.5.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.5.0"
+ "@babel/plugin-transform-modules-systemjs" "^7.5.0"
+ "@babel/plugin-transform-modules-umd" "^7.2.0"
+ "@babel/plugin-transform-named-capturing-groups-regex" "^7.4.5"
+ "@babel/plugin-transform-new-target" "^7.4.4"
+ "@babel/plugin-transform-object-super" "^7.5.5"
+ "@babel/plugin-transform-parameters" "^7.4.4"
+ "@babel/plugin-transform-property-literals" "^7.2.0"
+ "@babel/plugin-transform-regenerator" "^7.4.5"
+ "@babel/plugin-transform-reserved-words" "^7.2.0"
+ "@babel/plugin-transform-shorthand-properties" "^7.2.0"
+ "@babel/plugin-transform-spread" "^7.2.0"
+ "@babel/plugin-transform-sticky-regex" "^7.2.0"
+ "@babel/plugin-transform-template-literals" "^7.4.4"
+ "@babel/plugin-transform-typeof-symbol" "^7.2.0"
+ "@babel/plugin-transform-unicode-regex" "^7.4.4"
+ "@babel/types" "^7.5.5"
+ browserslist "^4.6.0"
+ core-js-compat "^3.1.1"
+ invariant "^2.2.2"
+ js-levenshtein "^1.1.3"
+ semver "^5.5.0"
+
+"@babel/register@^7.0.0":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.5.5.tgz#40fe0d474c8c8587b28d6ae18a03eddad3dac3c1"
+ integrity sha512-pdd5nNR+g2qDkXZlW1yRCWFlNrAn2PPdnZUB72zjX4l1Vv4fMRRLwyf+n/idFCLI1UgVGboUU8oVziwTBiyNKQ==
+ dependencies:
+ core-js "^3.0.0"
+ find-cache-dir "^2.0.0"
+ lodash "^4.17.13"
+ mkdirp "^0.5.1"
+ pirates "^4.0.0"
+ source-map-support "^0.5.9"
+
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
+ integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
+ dependencies:
+ regenerator-runtime "^0.13.2"
+
+"@babel/runtime@^7.2.0", "@babel/runtime@^7.5.5":
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.0.tgz#4fc1d642a9fd0299754e8b5de62c631cf5568205"
+ integrity sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==
+ dependencies:
+ regenerator-runtime "^0.13.2"
+
+"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237"
+ integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ "@babel/parser" "^7.4.4"
+ "@babel/types" "^7.4.4"
+
+"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb"
+ integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==
+ dependencies:
+ "@babel/code-frame" "^7.5.5"
+ "@babel/generator" "^7.5.5"
+ "@babel/helper-function-name" "^7.1.0"
+ "@babel/helper-split-export-declaration" "^7.4.4"
+ "@babel/parser" "^7.5.5"
+ "@babel/types" "^7.5.5"
+ debug "^4.1.0"
+ globals "^11.1.0"
+ lodash "^4.17.13"
+
+"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a"
+ integrity sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==
+ dependencies:
+ esutils "^2.0.2"
+ lodash "^4.17.13"
+ to-fast-properties "^2.0.0"
+
+"@expo/vector-icons@^10.0.2":
+ version "10.0.5"
+ resolved "https://registry.yarnpkg.com/@expo/vector-icons/-/vector-icons-10.0.5.tgz#2464fd262613ad11c9b2d5c4227e85c164fcccf4"
+ integrity sha512-SWdAx2Qzxp5TgT3hZEoF/KHnaDW7ajIFztrDdaDZl3nPo7ExK0YiQ03V0z0xMd+uQwl3SZO3JMwPZ7YnuxcMEg==
+ dependencies:
+ lodash "^4.17.4"
+
+"@expo/websql@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@expo/websql/-/websql-1.0.1.tgz#fff0cf9c1baa1f70f9e1d658b7c39a420d9b10a9"
+ integrity sha1-//DPnBuqH3D54dZYt8OaQg2bEKk=
+ dependencies:
+ argsarray "^0.0.1"
+ immediate "^3.2.2"
+ noop-fn "^1.0.0"
+ pouchdb-collections "^1.0.1"
+ tiny-queue "^0.2.1"
+
+"@react-native-community/cli@^1.2.1":
+ version "1.11.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-1.11.2.tgz#b14967f24a389f5a16889a747345cf0e5757a2f1"
+ integrity sha512-5NuYd30f5PCTrGUbZLnusZKv5nfTWvTDTRa/3Q4vwdMnUQrhm9sZXWGQ5CnFoQ7cE58EAqhj6/ShXeJF3DZ9uQ==
+ dependencies:
+ chalk "^1.1.1"
+ commander "^2.19.0"
+ compression "^1.7.1"
+ connect "^3.6.5"
+ denodeify "^1.2.1"
+ envinfo "^5.7.0"
+ errorhandler "^1.5.0"
+ escape-string-regexp "^1.0.5"
+ execa "^1.0.0"
+ fs-extra "^7.0.1"
+ glob "^7.1.1"
+ graceful-fs "^4.1.3"
+ inquirer "^3.0.6"
+ lodash "^4.17.5"
+ metro "^0.51.0"
+ metro-config "^0.51.0"
+ metro-core "^0.51.0"
+ metro-memory-fs "^0.51.0"
+ metro-react-native-babel-transformer "^0.51.0"
+ mime "^1.3.4"
+ minimist "^1.2.0"
+ mkdirp "^0.5.1"
+ morgan "^1.9.0"
+ node-fetch "^2.2.0"
+ node-notifier "^5.2.1"
+ opn "^3.0.2"
+ plist "^3.0.0"
+ semver "^5.0.3"
+ serve-static "^1.13.1"
+ shell-quote "1.6.1"
+ slash "^2.0.0"
+ ws "^1.1.0"
+ xcode "^2.0.0"
+ xmldoc "^0.4.0"
+
+"@react-native-community/netinfo@^4.1.5":
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-4.2.1.tgz#b6309f078da500807ef8afa4d659e56ccd14dee4"
+ integrity sha512-kAnmYp8vXpZToPw8rgE7uO+MqmqHSR9VEDPkuZT0DnFMBJmIXCSD2NLAD28HGKVY/kujVWCknC/FuVWr5/A3uA==
+
+"@react-navigation/core@~3.5.0":
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-3.5.0.tgz#73d1a12448e2bd71855e0080b95a7f51ede0cd9e"
+ integrity sha512-NLm24lA51R8o8c+iFnwtN9elqRzm4OJ8f1qPBCUNIYW1sb8M5yCD53vRP0fRcPFpr/6Xzs2TJMsWnnebwFp0Rw==
+ dependencies:
+ hoist-non-react-statics "^3.3.0"
+ path-to-regexp "^1.7.0"
+ query-string "^6.4.2"
+ react-is "^16.8.6"
+
+"@react-navigation/native@~3.6.1":
+ version "3.6.2"
+ resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-3.6.2.tgz#3634697b6350cc5189657ae4551f2d52b57fbbf0"
+ integrity sha512-Cybeou6N82ZeRmgnGlu+wzlV3z5BZQR2dmYaNFV1TNLUGHqtvv8E7oNw9uYcz9Ox5LFbiX+FdNTn2d6ZPlK0kg==
+ dependencies:
+ hoist-non-react-statics "^3.0.1"
+ react-native-safe-area-view "^0.14.1"
+ react-native-screens "^1.0.0 || ^1.0.0-alpha"
+
+"@redux-saga/core@^1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@redux-saga/core/-/core-1.0.3.tgz#c29ec7ae3d7dfcbcb8dc93177f2b3edc798c5d85"
+ integrity sha512-zf8h5N0oTzaNeSMxOWH9GJMB9IRSM8JubDsrZVsvVltXjzFFSR8DNt7tbPoRJUK0hFfQB1it+bL+dEMWpD7wXA==
+ dependencies:
+ "@babel/runtime" "^7.0.0"
+ "@redux-saga/deferred" "^1.0.1"
+ "@redux-saga/delay-p" "^1.0.1"
+ "@redux-saga/is" "^1.0.2"
+ "@redux-saga/symbols" "^1.0.1"
+ "@redux-saga/types" "^1.0.2"
+ redux ">=0.10 <5"
+ typescript-tuple "^2.1.0"
+
+"@redux-saga/deferred@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@redux-saga/deferred/-/deferred-1.0.1.tgz#c895445e486bded90acf0b873b4e978fbfe458c2"
+ integrity sha512-+gW5xQ93QXOOmRLAmX8x2Hx1HpbTG6CM6+HcdTSbJovh4uMWaGyeDECnqXSt8QqA/ja3s2nqYXLqXFKepIQ1hw==
+
+"@redux-saga/delay-p@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@redux-saga/delay-p/-/delay-p-1.0.1.tgz#d69fc6103c7509ae80faa144ea17bbc69e51e029"
+ integrity sha512-0SnNDyDLUyB4NThtptAwiprNOnbCNhoed/Rp5JwS7SB+a/AdWynVgg/E6BmjsggLFNr07KW0bzn05tsPRBuU7Q==
+ dependencies:
+ "@redux-saga/symbols" "^1.0.1"
+
+"@redux-saga/is@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@redux-saga/is/-/is-1.0.2.tgz#7f4be014c97061898d7efb11d6c9de31e943ed38"
+ integrity sha512-WnaUOwYvPK2waWjzebT4uhL8zY76XNkzzpJ2EQJe8bN1tByvAjvT7MuJZTSshOhdHL5PsRO0MsH224XIXBJidQ==
+ dependencies:
+ "@redux-saga/symbols" "^1.0.1"
+ "@redux-saga/types" "^1.0.2"
+
+"@redux-saga/symbols@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@redux-saga/symbols/-/symbols-1.0.1.tgz#46512ae1275f88df061c42168d0f600ddb170c1e"
+ integrity sha512-akKkzcVnb1RzJaZV2LQFbi51abvdICMuAKwwLoCjjxLbLAGIw9EJxk5ucNnWSSCEsoEQMeol5tkAcK+Xzuv1Bg==
+
+"@redux-saga/types@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@redux-saga/types/-/types-1.0.2.tgz#1d94f02800b094753f9271c206a26c2a06ca14ee"
+ integrity sha512-8/qcMh15507AnXJ3lBeuhsdFwnWQqnp68EpUuHlYPixJ5vjVmls7/Jq48cnUlrZI8Jd9U1jkhfCl0gaT5KMgVw==
+
+"@types/fbemitter@^2.0.32":
+ version "2.0.32"
+ resolved "https://registry.yarnpkg.com/@types/fbemitter/-/fbemitter-2.0.32.tgz#8ed204da0f54e9c8eaec31b1eec91e25132d082c"
+ integrity sha1-jtIE2g9U6cjq7DGx7skeJRMtCCw=
+
+"@types/invariant@^2.2.29":
+ version "2.2.30"
+ resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.30.tgz#20efa342807606ada5483731a8137cb1561e5fe9"
+ integrity sha512-98fB+yo7imSD2F7PF7GIpELNgtLNgo5wjivu0W5V4jx+KVVJxo6p/qN4zdzSTBWy4/sN3pPyXwnhRSD28QX+ag==
+
+"@types/lodash.zipobject@^4.1.4":
+ version "4.1.6"
+ resolved "https://registry.yarnpkg.com/@types/lodash.zipobject/-/lodash.zipobject-4.1.6.tgz#75e140f44ac7d7682a18d3aae8ee4594fad094d7"
+ integrity sha512-30khEHqHWaLgMZR35wtkg07OmHiNiDQyor0SK7oj8Sy05tg6jDjPmJybeZ64WKeFZUEgs1tdJwdT0xUl+2qUgQ==
+ dependencies:
+ "@types/lodash" "*"
+
+"@types/lodash@*":
+ version "4.14.137"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.137.tgz#8a4804937dc6462274ffcc088df8f14fc1b368e2"
+ integrity sha512-g4rNK5SRKloO+sUGbuO7aPtwbwzMgjK+bm9BBhLD7jGUiGR7zhwYEhSln/ihgYQBeIJ5j7xjyaYzrWTcu3UotQ==
+
+"@types/prop-types@*":
+ version "15.7.2"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.2.tgz#0e58ae66773d7fd7c372a493aff740878ec9ceaa"
+ integrity sha512-f8JzJNWVhKtc9dg/dyDNfliTKNOJSLa7Oht/ElZdF/UbMUmAH3rLmAk3ODNjw0mZajDEgatA03tRjB4+Dp/tzA==
+
+"@types/qs@^6.5.1":
+ version "6.5.3"
+ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.5.3.tgz#1c3b71b091eaeaf5924538006b7f70603ce63d38"
+ integrity sha512-Jugo5V/1bS0fRhy2z8+cUAHEyWOATaz4rbyLVvcFs7+dXp5HfwpEwzF1Q11bB10ApUqHf+yTauxI0UXQDwGrbA==
+
+"@types/react-native-vector-icons@^6.4.1":
+ version "6.4.1"
+ resolved "https://registry.yarnpkg.com/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.1.tgz#d0862f3eb97bbe5720ab604f2f10ee07dacb31ef"
+ integrity sha512-2C9jyyJKEjzX0C4nP+9rNNjEhutWwalkRdaSTEYtNjUkkkTk68WGG2hgjrcBjdm4Gn1Vi9OgUd+FR8GNmCyp+A==
+ dependencies:
+ "@types/react" "*"
+ "@types/react-native" "*"
+
+"@types/react-native@*":
+ version "0.60.13"
+ resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.60.13.tgz#381703a787db0f0d49895f3a17b50474f4024551"
+ integrity sha512-iEydUatGCGE6s5DSUcqnBGjruxor+XUb+52FTjKXvUDaisXoFZHei4yvAMAVrI7LMvVU0DQUlmHUAkoveO/fNQ==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/react" "*"
+
+"@types/react@*":
+ version "16.9.2"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.2.tgz#6d1765431a1ad1877979013906731aae373de268"
+ integrity sha512-jYP2LWwlh+FTqGd9v7ynUKZzjj98T8x7Yclz479QdRhHfuW9yQ+0jjnD31eXSXutmBpppj5PYNLYLRfnZJvcfg==
+ dependencies:
+ "@types/prop-types" "*"
+ csstype "^2.2.0"
+
+"@types/uuid-js@^0.7.1":
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/@types/uuid-js/-/uuid-js-0.7.2.tgz#5b5552fcbaaf4acf026fb6dc66f7e5bd6b4be92f"
+ integrity sha512-9R+mA6mMXkFVQnXEeX5fMQDR2SYND7cafJTqbeMpLhgsL7qr7MF4ZBxWpLexml3lZsBsyAmqVWbOiB0N10m15w==
+
+"@types/websql@^0.0.27":
+ version "0.0.27"
+ resolved "https://registry.yarnpkg.com/@types/websql/-/websql-0.0.27.tgz#621a666a7f02018e7cbb4abab956a25736c27d71"
+ integrity sha1-Yhpman8CAY58u0q6uVaiVzbCfXE=
+
+"@unimodules/core@~3.0.0":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@unimodules/core/-/core-3.0.2.tgz#a2b143fb1e743809ba17c60ae1848f82b8637901"
+ integrity sha512-EMZjVp+yrtoPKpDBPvj4+hyDWALl7gvpWeUsDz2Nb9MMBPLnhag1uNk3KC98StJdnjbSXKSdKrCMMidOXnyKcg==
+ dependencies:
+ compare-versions "^3.4.0"
+
+"@unimodules/react-native-adapter@~3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@unimodules/react-native-adapter/-/react-native-adapter-3.0.0.tgz#303b76c131fe6b5ceb220235ddd1fa2a0193403d"
+ integrity sha512-zkFFE0HQ2Flfx/aY3hBKDgMvQ1meUm3H6vMIacY1KywexCuKW8ivBobkOsHIet4jf7km0Eklt6WtB3LqQVw5yw==
+ dependencies:
+ invariant "^2.2.4"
+ lodash "^4.5.0"
+ prop-types "^15.6.1"
+
+abbrev@1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
+ integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
+
+absolute-path@^0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7"
+ integrity sha1-p4di+9rftSl76ZsV01p4Wy8JW/c=
+
+accepts@~1.3.5, accepts@~1.3.7:
+ version "1.3.7"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
+ integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
+ dependencies:
+ mime-types "~2.1.24"
+ negotiator "0.6.2"
+
+acorn-jsx@^5.0.2:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f"
+ integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==
+
+acorn@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a"
+ integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==
+
+ajv@^6.10.0, ajv@^6.10.2:
+ version "6.10.2"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
+ integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
+ dependencies:
+ fast-deep-equal "^2.0.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ansi-colors@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9"
+ integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==
+ dependencies:
+ ansi-wrap "^0.1.0"
+
+ansi-cyan@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873"
+ integrity sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=
+ dependencies:
+ ansi-wrap "0.1.0"
+
+ansi-escapes@^3.0.0, ansi-escapes@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
+ integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
+
+ansi-gray@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251"
+ integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE=
+ dependencies:
+ ansi-wrap "0.1.0"
+
+ansi-red@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c"
+ integrity sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=
+ dependencies:
+ ansi-wrap "0.1.0"
+
+ansi-regex@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+ integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
+
+ansi-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+ integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
+
+ansi-regex@^4.0.0, ansi-regex@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
+ integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
+
+ansi-styles@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+ integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
+
+ansi-styles@^3.2.0, ansi-styles@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+ dependencies:
+ color-convert "^1.9.0"
+
+ansi-wrap@0.1.0, ansi-wrap@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
+ integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768=
+
+ansi@^0.3.0, ansi@~0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/ansi/-/ansi-0.3.1.tgz#0c42d4fb17160d5a9af1e484bace1c66922c1b21"
+ integrity sha1-DELU+xcWDVqa8eSEus4cZpIsGyE=
+
+anymatch@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
+ integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==
+ dependencies:
+ micromatch "^3.1.4"
+ normalize-path "^2.1.1"
+
+aproba@^1.0.3:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
+ integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
+
+are-we-there-yet@~1.1.2:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
+ integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
+ dependencies:
+ delegates "^1.0.0"
+ readable-stream "^2.0.6"
+
+argparse@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+ dependencies:
+ sprintf-js "~1.0.2"
+
+argsarray@^0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/argsarray/-/argsarray-0.0.1.tgz#6e7207b4ecdb39b0af88303fa5ae22bda8df61cb"
+ integrity sha1-bnIHtOzbObCviDA/pa4ivajfYcs=
+
+arr-diff@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a"
+ integrity sha1-aHwydYFjWI/vfeezb6vklesaOZo=
+ dependencies:
+ arr-flatten "^1.0.1"
+ array-slice "^0.2.3"
+
+arr-diff@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
+ integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=
+ dependencies:
+ arr-flatten "^1.0.1"
+
+arr-diff@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
+ integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
+
+arr-flatten@^1.0.1, arr-flatten@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+ integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
+
+arr-union@^2.0.1:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-2.1.0.tgz#20f9eab5ec70f5c7d215b1077b1c39161d292c7d"
+ integrity sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=
+
+arr-union@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
+ integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
+
+array-filter@~0.0.0:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
+ integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw=
+
+array-find-index@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
+ integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
+
+array-map@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
+ integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=
+
+array-reduce@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b"
+ integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=
+
+array-slice@^0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5"
+ integrity sha1-3Tz7gO15c6dRF82sabC5nshhhvU=
+
+array-unique@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
+ integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=
+
+array-unique@^0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
+ integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
+
+art@^0.10.0:
+ version "0.10.3"
+ resolved "https://registry.yarnpkg.com/art/-/art-0.10.3.tgz#b01d84a968ccce6208df55a733838c96caeeaea2"
+ integrity sha512-HXwbdofRTiJT6qZX/FnchtldzJjS3vkLJxQilc3Xj+ma2MXjY4UAyQ0ls1XZYVnDvVIBiFZbC6QsvtW86TD6tQ==
+
+asap@~2.0.3:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
+ integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
+
+assign-symbols@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
+ integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
+
+astral-regex@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
+ integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
+
+async-limiter@~1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
+ integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
+
+async@^2.4.0:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
+ integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
+ dependencies:
+ lodash "^4.17.14"
+
+atob@^2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+ integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
+babel-plugin-dotenv@0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-dotenv/-/babel-plugin-dotenv-0.1.1.tgz#9c8faea67a7c034fe7e94099187ab2e7573400bc"
+ integrity sha1-nI+upnp8A0/n6UCZGHqy51c0ALw=
+ dependencies:
+ dotenv "^2.0.0"
+
+babel-plugin-dynamic-import-node@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f"
+ integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==
+ dependencies:
+ object.assign "^4.1.0"
+
+babel-plugin-module-resolver@^3.1.1:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.2.0.tgz#ddfa5e301e3b9aa12d852a9979f18b37881ff5a7"
+ integrity sha512-tjR0GvSndzPew/Iayf4uICWZqjBwnlMWjSx6brryfQ81F9rxBVqwDJtFCV8oOs0+vJeefK9TmdZtkIFdFe1UnA==
+ dependencies:
+ find-babel-config "^1.1.0"
+ glob "^7.1.2"
+ pkg-up "^2.0.0"
+ reselect "^3.0.1"
+ resolve "^1.4.0"
+
+babel-plugin-react-native-web@^0.11.2:
+ version "0.11.7"
+ resolved "https://registry.yarnpkg.com/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.11.7.tgz#15b578c0731bd7d65d334f9c759d95e8e4a602e2"
+ integrity sha512-CxE7uhhqkzAFkwV2X7+Mc/UVPujQQDtja/EGxCXRJvdYRi72QTmaJYKbK1lV9qgTZuB+TDguU89coaA9Z1BNbg==
+
+babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0:
+ version "7.0.0-beta.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf"
+ integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==
+
+babel-preset-expo@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-6.0.0.tgz#acc4eb8343a2f703d5808916c051a6caefde8778"
+ integrity sha512-MvDy86afmCt4sFYkg7yXsZyGL0yONT5JQHZSK1r8cu26Zm1No0yQyll+w78e2OkkYwVFtC1u70GyBPdERG7BZg==
+ dependencies:
+ "@babel/core" "^7.1.0"
+ "@babel/plugin-proposal-decorators" "^7.1.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.4.4"
+ "@babel/preset-env" "^7.3.1"
+ babel-plugin-module-resolver "^3.1.1"
+ babel-plugin-react-native-web "^0.11.2"
+ metro-react-native-babel-preset "^0.51.1"
+
+babel-preset-fbjs@^3.0.1, babel-preset-fbjs@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.2.0.tgz#c0e6347d3e0379ed84b3c2434d3467567aa05297"
+ integrity sha512-5Jo+JeWiVz2wHUUyAlvb/sSYnXNig9r+HqGAOSfh5Fzxp7SnAaR/tEGRJ1ZX7C77kfk82658w6R5Z+uPATTD9g==
+ dependencies:
+ "@babel/plugin-proposal-class-properties" "^7.0.0"
+ "@babel/plugin-proposal-object-rest-spread" "^7.0.0"
+ "@babel/plugin-syntax-class-properties" "^7.0.0"
+ "@babel/plugin-syntax-flow" "^7.0.0"
+ "@babel/plugin-syntax-jsx" "^7.0.0"
+ "@babel/plugin-syntax-object-rest-spread" "^7.0.0"
+ "@babel/plugin-transform-arrow-functions" "^7.0.0"
+ "@babel/plugin-transform-block-scoped-functions" "^7.0.0"
+ "@babel/plugin-transform-block-scoping" "^7.0.0"
+ "@babel/plugin-transform-classes" "^7.0.0"
+ "@babel/plugin-transform-computed-properties" "^7.0.0"
+ "@babel/plugin-transform-destructuring" "^7.0.0"
+ "@babel/plugin-transform-flow-strip-types" "^7.0.0"
+ "@babel/plugin-transform-for-of" "^7.0.0"
+ "@babel/plugin-transform-function-name" "^7.0.0"
+ "@babel/plugin-transform-literals" "^7.0.0"
+ "@babel/plugin-transform-member-expression-literals" "^7.0.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.0.0"
+ "@babel/plugin-transform-object-super" "^7.0.0"
+ "@babel/plugin-transform-parameters" "^7.0.0"
+ "@babel/plugin-transform-property-literals" "^7.0.0"
+ "@babel/plugin-transform-react-display-name" "^7.0.0"
+ "@babel/plugin-transform-react-jsx" "^7.0.0"
+ "@babel/plugin-transform-shorthand-properties" "^7.0.0"
+ "@babel/plugin-transform-spread" "^7.0.0"
+ "@babel/plugin-transform-template-literals" "^7.0.0"
+ babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0"
+
+balanced-match@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+ integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+base-64@0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb"
+ integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs=
+
+base64-js@^1.1.2, base64-js@^1.2.3:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
+ integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
+
+base@^0.11.1:
+ version "0.11.2"
+ resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
+ integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
+ dependencies:
+ cache-base "^1.0.1"
+ class-utils "^0.3.5"
+ component-emitter "^1.2.1"
+ define-property "^1.0.0"
+ isobject "^3.0.1"
+ mixin-deep "^1.2.0"
+ pascalcase "^0.1.1"
+
+basic-auth@~2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
+ integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==
+ dependencies:
+ safe-buffer "5.1.2"
+
+big-integer@^1.6.7:
+ version "1.6.44"
+ resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.44.tgz#4ee9ae5f5839fc11ade338fea216b4513454a539"
+ integrity sha512-7MzElZPTyJ2fNvBkPxtFQ2fWIkVmuzw41+BZHSzpEq3ymB2MfeKp1+yXl/tS75xCx+WnyV+yb0kp+K1C3UNwmQ==
+
+blueimp-md5@^2.10.0:
+ version "2.11.1"
+ resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.11.1.tgz#b1f6b6218d13cdedbf5743f32b3023b2afefcbd3"
+ integrity sha512-4UiOAmql2XO0Sws07OVzYdCKK0K2Va5g6AVgYXoGhEQiKrdSOefjUCm1frPk6E+xiIOHRqaFg+TUGo7cClKg5g==
+
+bplist-creator@0.0.7:
+ version "0.0.7"
+ resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.0.7.tgz#37df1536092824b87c42f957b01344117372ae45"
+ integrity sha1-N98VNgkoJLh8QvlXsBNEEXNyrkU=
+ dependencies:
+ stream-buffers "~2.2.0"
+
+bplist-parser@0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.1.1.tgz#d60d5dcc20cba6dc7e1f299b35d3e1f95dafbae6"
+ integrity sha1-1g1dzCDLptx+HymbNdPh+V2vuuY=
+ dependencies:
+ big-integer "^1.6.7"
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+braces@^1.8.2:
+ version "1.8.5"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
+ integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=
+ dependencies:
+ expand-range "^1.8.1"
+ preserve "^0.2.0"
+ repeat-element "^1.1.2"
+
+braces@^2.3.1:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
+ integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
+ dependencies:
+ arr-flatten "^1.1.0"
+ array-unique "^0.3.2"
+ extend-shallow "^2.0.1"
+ fill-range "^4.0.0"
+ isobject "^3.0.1"
+ repeat-element "^1.1.2"
+ snapdragon "^0.8.1"
+ snapdragon-node "^2.0.1"
+ split-string "^3.0.2"
+ to-regex "^3.0.1"
+
+browserslist@^4.6.0, browserslist@^4.6.6:
+ version "4.6.6"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.6.tgz#6e4bf467cde520bc9dbdf3747dafa03531cec453"
+ integrity sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA==
+ dependencies:
+ caniuse-lite "^1.0.30000984"
+ electron-to-chromium "^1.3.191"
+ node-releases "^1.1.25"
+
+bser@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.0.tgz#65fc784bf7f87c009b973c12db6546902fa9c7b5"
+ integrity sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg==
+ dependencies:
+ node-int64 "^0.4.0"
+
+buffer-alloc-unsafe@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
+ integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
+
+buffer-alloc@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
+ integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
+ dependencies:
+ buffer-alloc-unsafe "^1.1.0"
+ buffer-fill "^1.0.0"
+
+buffer-crc32@^0.2.13:
+ version "0.2.13"
+ resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
+ integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
+
+buffer-fill@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
+ integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
+
+buffer-from@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+ integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
+
+bytes@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+ integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
+
+cache-base@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
+ integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
+ dependencies:
+ collection-visit "^1.0.0"
+ component-emitter "^1.2.1"
+ get-value "^2.0.6"
+ has-value "^1.0.0"
+ isobject "^3.0.1"
+ set-value "^2.0.0"
+ to-object-path "^0.3.0"
+ union-value "^1.0.0"
+ unset-value "^1.0.0"
+
+caller-callsite@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
+ integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=
+ dependencies:
+ callsites "^2.0.0"
+
+caller-path@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
+ integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=
+ dependencies:
+ caller-callsite "^2.0.0"
+
+callsites@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
+ integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
+
+callsites@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+camelcase@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
+ integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
+
+camelcase@^5.0.0:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+ integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
+caniuse-lite@^1.0.30000984:
+ version "1.0.30000989"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz#b9193e293ccf7e4426c5245134b8f2a56c0ac4b9"
+ integrity sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw==
+
+capture-exit@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-1.2.0.tgz#1c5fcc489fd0ab00d4f1ac7ae1072e3173fbab6f"
+ integrity sha1-HF/MSJ/QqwDU8ax64QcuMXP7q28=
+ dependencies:
+ rsvp "^3.3.3"
+
+chalk@^1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+ integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
+ dependencies:
+ ansi-styles "^2.2.1"
+ escape-string-regexp "^1.0.2"
+ has-ansi "^2.0.0"
+ strip-ansi "^3.0.0"
+ supports-color "^2.0.0"
+
+chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+ integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+ dependencies:
+ ansi-styles "^3.2.1"
+ escape-string-regexp "^1.0.5"
+ supports-color "^5.3.0"
+
+chardet@^0.4.0:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
+ integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=
+
+chardet@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
+ integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
+
+chownr@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6"
+ integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==
+
+class-utils@^0.3.5:
+ version "0.3.6"
+ resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
+ integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
+ dependencies:
+ arr-union "^3.1.0"
+ define-property "^0.2.5"
+ isobject "^3.0.0"
+ static-extend "^0.1.1"
+
+cli-cursor@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+ integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
+ dependencies:
+ restore-cursor "^2.0.0"
+
+cli-width@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
+ integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
+
+cliui@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
+ integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+ wrap-ansi "^2.0.0"
+
+cliui@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
+ integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==
+ dependencies:
+ string-width "^3.1.0"
+ strip-ansi "^5.2.0"
+ wrap-ansi "^5.1.0"
+
+code-point-at@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+ integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
+
+collection-visit@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
+ integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
+ dependencies:
+ map-visit "^1.0.0"
+ object-visit "^1.0.0"
+
+color-convert@^1.9.0, color-convert@^1.9.1:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+ dependencies:
+ color-name "1.1.3"
+
+color-name@1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+ integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+color-name@^1.0.0:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+color-string@^1.5.2:
+ version "1.5.3"
+ resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc"
+ integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==
+ dependencies:
+ color-name "^1.0.0"
+ simple-swizzle "^0.2.2"
+
+color-support@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
+ integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
+
+color@^3.1.0:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10"
+ integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==
+ dependencies:
+ color-convert "^1.9.1"
+ color-string "^1.5.2"
+
+commander@^2.19.0, commander@^2.9.0:
+ version "2.20.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
+ integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
+
+commander@~2.13.0:
+ version "2.13.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
+ integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==
+
+commondir@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+ integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
+
+compare-versions@^3.4.0:
+ version "3.5.1"
+ resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.5.1.tgz#26e1f5cf0d48a77eced5046b9f67b6b61075a393"
+ integrity sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg==
+
+component-emitter@^1.2.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
+ integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
+
+compressible@~2.0.16:
+ version "2.0.17"
+ resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1"
+ integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==
+ dependencies:
+ mime-db ">= 1.40.0 < 2"
+
+compression@^1.7.1:
+ version "1.7.4"
+ resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
+ integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
+ dependencies:
+ accepts "~1.3.5"
+ bytes "3.0.0"
+ compressible "~2.0.16"
+ debug "2.6.9"
+ on-headers "~1.0.2"
+ safe-buffer "5.1.2"
+ vary "~1.1.2"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+concat-stream@^1.6.0:
+ version "1.6.2"
+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
+ integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
+ dependencies:
+ buffer-from "^1.0.0"
+ inherits "^2.0.3"
+ readable-stream "^2.2.2"
+ typedarray "^0.0.6"
+
+connect@^3.6.5:
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8"
+ integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==
+ dependencies:
+ debug "2.6.9"
+ finalhandler "1.1.2"
+ parseurl "~1.3.3"
+ utils-merge "1.0.1"
+
+console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+ integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
+
+convert-source-map@^1.1.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"
+ integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==
+ dependencies:
+ safe-buffer "~5.1.1"
+
+copy-descriptor@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
+ integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
+
+core-js-compat@^3.1.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.2.1.tgz#0cbdbc2e386e8e00d3b85dc81c848effec5b8150"
+ integrity sha512-MwPZle5CF9dEaMYdDeWm73ao/IflDH+FjeJCWEADcEgFSE9TLimFKwJsfmkwzI8eC0Aj0mgvMDjeQjrElkz4/A==
+ dependencies:
+ browserslist "^4.6.6"
+ semver "^6.3.0"
+
+core-js@^1.0.0:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
+ integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
+
+core-js@^2.2.2, core-js@^2.4.1:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2"
+ integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==
+
+core-js@^3.0.0:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.2.1.tgz#cd41f38534da6cc59f7db050fe67307de9868b09"
+ integrity sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw==
+
+core-util-is@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+ integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+
+cosmiconfig@^5.0.5:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
+ integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
+ dependencies:
+ import-fresh "^2.0.0"
+ is-directory "^0.3.1"
+ js-yaml "^3.13.1"
+ parse-json "^4.0.0"
+
+create-react-class@^15.6.2, create-react-class@^15.6.3:
+ version "15.6.3"
+ resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036"
+ integrity sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==
+ dependencies:
+ fbjs "^0.8.9"
+ loose-envify "^1.3.1"
+ object-assign "^4.1.1"
+
+cross-spawn@^5.0.1, cross-spawn@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
+ integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
+ dependencies:
+ lru-cache "^4.0.1"
+ shebang-command "^1.2.0"
+ which "^1.2.9"
+
+cross-spawn@^6.0.0, cross-spawn@^6.0.5:
+ version "6.0.5"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+ integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
+ dependencies:
+ nice-try "^1.0.4"
+ path-key "^2.0.1"
+ semver "^5.5.0"
+ shebang-command "^1.2.0"
+ which "^1.2.9"
+
+css-in-js-utils@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99"
+ integrity sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==
+ dependencies:
+ hyphenate-style-name "^1.0.2"
+ isobject "^3.0.1"
+
+csstype@^2.2.0:
+ version "2.6.6"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.6.tgz#c34f8226a94bbb10c32cc0d714afdf942291fc41"
+ integrity sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg==
+
+debounce@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131"
+ integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==
+
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+debug@^3.2.6:
+ version "3.2.6"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
+ integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+ dependencies:
+ ms "^2.1.1"
+
+debug@^4.0.1, debug@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
+ integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
+ dependencies:
+ ms "^2.1.1"
+
+decamelize@^1.1.1, decamelize@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+ integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
+
+decode-uri-component@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+ integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+
+deep-assign@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/deep-assign/-/deep-assign-3.0.0.tgz#c8e4c4d401cba25550a2f0f486a2e75bc5f219a2"
+ integrity sha512-YX2i9XjJ7h5q/aQ/IM9PEwEnDqETAIYbggmdDB3HLTlSgo1CxPsj6pvhPG68rq6SVE0+p+6Ywsm5fTYNrYtBWw==
+ dependencies:
+ is-obj "^1.0.0"
+
+deep-extend@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
+ integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+
+deep-is@~0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+ integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+
+deepmerge@^3.1.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.3.0.tgz#d3c47fd6f3a93d517b14426b0628a17b0125f5f7"
+ integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==
+
+define-properties@^1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
+ integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
+ dependencies:
+ object-keys "^1.0.12"
+
+define-property@^0.2.5:
+ version "0.2.5"
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
+ integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
+ dependencies:
+ is-descriptor "^0.1.0"
+
+define-property@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
+ integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
+ dependencies:
+ is-descriptor "^1.0.0"
+
+define-property@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
+ integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
+ dependencies:
+ is-descriptor "^1.0.2"
+ isobject "^3.0.1"
+
+delegates@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+ integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
+
+denodeify@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631"
+ integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE=
+
+depd@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+ integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
+
+destroy@~1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
+ integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
+
+detect-libc@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+ integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
+
+doctrine@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+ integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+ dependencies:
+ esutils "^2.0.2"
+
+dom-walk@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
+ integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=
+
+dotenv@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-2.0.0.tgz#bd759c357aaa70365e01c96b7b0bec08a6e0d949"
+ integrity sha1-vXWcNXqqcDZeAclrewvsCKbg2Uk=
+
+ee-first@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+ integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
+
+electron-to-chromium@^1.3.191:
+ version "1.3.241"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.241.tgz#859dc49ab7f90773ed698767372d384190f60cb1"
+ integrity sha512-Gb9E6nWZlbgjDDNe5cAvMJixtn79krNJ70EDpq/M10lkGo7PGtBUe7Y0CYVHsBScRwi6ybCS+YetXAN9ysAHDg==
+
+emoji-regex@^7.0.1:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
+ integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
+
+encodeurl@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+ integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
+
+encoding@^0.1.11:
+ version "0.1.12"
+ resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
+ integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=
+ dependencies:
+ iconv-lite "~0.4.13"
+
+end-of-stream@^1.1.0:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
+ integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
+ dependencies:
+ once "^1.4.0"
+
+envinfo@^5.7.0:
+ version "5.12.1"
+ resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-5.12.1.tgz#83068c33e0972eb657d6bc69a6df30badefb46ef"
+ integrity sha512-pwdo0/G3CIkQ0y6PCXq4RdkvId2elvtPCJMG0konqlrfkWQbf1DWeH9K2b/cvu2YgGvPPTOnonZxXM1gikFu1w==
+
+error-ex@^1.2.0, error-ex@^1.3.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+ integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+ dependencies:
+ is-arrayish "^0.2.1"
+
+errorhandler@^1.5.0:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/errorhandler/-/errorhandler-1.5.1.tgz#b9ba5d17cf90744cd1e851357a6e75bf806a9a91"
+ integrity sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==
+ dependencies:
+ accepts "~1.3.7"
+ escape-html "~1.0.3"
+
+es6-error@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
+ integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
+
+escape-html@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+ integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+eslint-config-prettier@6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.3.0.tgz#e73b48e59dc49d950843f3eb96d519e2248286a3"
+ integrity sha512-EWaGjlDAZRzVFveh2Jsglcere2KK5CJBhkNSa1xs3KfMUGdRiT7lG089eqPdvlzWHpAqaekubOsOMu8W8Yk71A==
+ dependencies:
+ get-stdin "^6.0.0"
+
+eslint-plugin-prettier@3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz#8695188f95daa93b0dc54b249347ca3b79c4686d"
+ integrity sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==
+ dependencies:
+ prettier-linter-helpers "^1.0.0"
+
+eslint-scope@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9"
+ integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==
+ dependencies:
+ esrecurse "^4.1.0"
+ estraverse "^4.1.1"
+
+eslint-utils@^1.4.2:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab"
+ integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==
+ dependencies:
+ eslint-visitor-keys "^1.0.0"
+
+eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
+ integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==
+
+eslint@6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.3.0.tgz#1f1a902f67bfd4c354e7288b81e40654d927eb6a"
+ integrity sha512-ZvZTKaqDue+N8Y9g0kp6UPZtS4FSY3qARxBs7p4f0H0iof381XHduqVerFWtK8DPtKmemqbqCFENWSQgPR/Gow==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ ajv "^6.10.0"
+ chalk "^2.1.0"
+ cross-spawn "^6.0.5"
+ debug "^4.0.1"
+ doctrine "^3.0.0"
+ eslint-scope "^5.0.0"
+ eslint-utils "^1.4.2"
+ eslint-visitor-keys "^1.1.0"
+ espree "^6.1.1"
+ esquery "^1.0.1"
+ esutils "^2.0.2"
+ file-entry-cache "^5.0.1"
+ functional-red-black-tree "^1.0.1"
+ glob-parent "^5.0.0"
+ globals "^11.7.0"
+ ignore "^4.0.6"
+ import-fresh "^3.0.0"
+ imurmurhash "^0.1.4"
+ inquirer "^6.4.1"
+ is-glob "^4.0.0"
+ js-yaml "^3.13.1"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ levn "^0.3.0"
+ lodash "^4.17.14"
+ minimatch "^3.0.4"
+ mkdirp "^0.5.1"
+ natural-compare "^1.4.0"
+ optionator "^0.8.2"
+ progress "^2.0.0"
+ regexpp "^2.0.1"
+ semver "^6.1.2"
+ strip-ansi "^5.2.0"
+ strip-json-comments "^3.0.1"
+ table "^5.2.3"
+ text-table "^0.2.0"
+ v8-compile-cache "^2.0.3"
+
+espree@^6.1.1:
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de"
+ integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ==
+ dependencies:
+ acorn "^7.0.0"
+ acorn-jsx "^5.0.2"
+ eslint-visitor-keys "^1.1.0"
+
+esprima@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+esquery@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
+ integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==
+ dependencies:
+ estraverse "^4.0.0"
+
+esrecurse@^4.1.0:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
+ integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==
+ dependencies:
+ estraverse "^4.1.0"
+
+estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+esutils@^2.0.0, esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+etag@~1.8.1:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+ integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
+
+event-target-shim@^1.0.5:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-1.1.1.tgz#a86e5ee6bdaa16054475da797ccddf0c55698491"
+ integrity sha1-qG5e5r2qFgVEddp5fM3fDFVphJE=
+
+eventemitter3@^3.0.0:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
+ integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==
+
+exec-sh@^0.2.0:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.2.tgz#2a5e7ffcbd7d0ba2755bdecb16e5a427dfbdec36"
+ integrity sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==
+ dependencies:
+ merge "^1.2.0"
+
+execa@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
+ integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=
+ dependencies:
+ cross-spawn "^5.0.1"
+ get-stream "^3.0.0"
+ is-stream "^1.1.0"
+ npm-run-path "^2.0.0"
+ p-finally "^1.0.0"
+ signal-exit "^3.0.0"
+ strip-eof "^1.0.0"
+
+execa@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
+ integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
+ dependencies:
+ cross-spawn "^6.0.0"
+ get-stream "^4.0.0"
+ is-stream "^1.1.0"
+ npm-run-path "^2.0.0"
+ p-finally "^1.0.0"
+ signal-exit "^3.0.0"
+ strip-eof "^1.0.0"
+
+expand-brackets@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
+ integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=
+ dependencies:
+ is-posix-bracket "^0.1.0"
+
+expand-brackets@^2.1.4:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
+ integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
+ dependencies:
+ debug "^2.3.3"
+ define-property "^0.2.5"
+ extend-shallow "^2.0.1"
+ posix-character-classes "^0.1.0"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.1"
+
+expand-range@^1.8.1:
+ version "1.8.2"
+ resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
+ integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=
+ dependencies:
+ fill-range "^2.1.0"
+
+expo-app-auth@~6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/expo-app-auth/-/expo-app-auth-6.0.0.tgz#4b2535da16a0234f67e813708960fc146aec867e"
+ integrity sha512-/7dv/RXYfg4OGmm5xOJUTecezTqccxmaUuYx1jt+hoI/QqD+q5uvT2u5Phi8uU4WsE5bi8qlkSRNTpL+rGSVoQ==
+ dependencies:
+ invariant "^2.2.4"
+
+expo-app-loader-provider@~6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/expo-app-loader-provider/-/expo-app-loader-provider-6.0.0.tgz#c187a39942ac27cfaec3b394a5c9851d3f39678b"
+ integrity sha512-GtpztJVxOz+vVwdLyHskpzVzFWMXZPIFC/zczHZPsTwjS+wXj6n8MVaLxX6GaTyhNEtYjp0VIQUw3b7eP+vO6w==
+
+expo-asset@~6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-6.0.0.tgz#caa3f45e7a27d978f8055fc58df6e33a4e661937"
+ integrity sha512-M0sJphdCQ0mq+7kg6rQmq4rU5hbsL72AZCNrga565JchCLeevJhv6j72erh2viqDAPdvjZpGwc7pwI/dxu1+zg==
+ dependencies:
+ blueimp-md5 "^2.10.0"
+ path-browserify "^1.0.0"
+ url-parse "^1.4.4"
+
+expo-constants@~6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-6.0.0.tgz#ff4598985ba006e24949be3f05be7429e5bedacf"
+ integrity sha512-O0yL3Ok0YUEWpAqsWjOdgFD/lMfg8PUGH/nq31CZ1s7cuFUlksD42i5YhIRlb0Pa/btK8X9LpfY3eWhx9eTmbg==
+ dependencies:
+ ua-parser-js "^0.7.19"
+
+expo-file-system@~6.0.0:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-6.0.2.tgz#e65f30eb6a7213e07933df9688116eaf4e25bbf8"
+ integrity sha512-s+6oQpLhcT7MQp7fcoj1E+zttMr0WX6c0FrddzqB4dUfhIggV+nb35nQMASIiTHAj8VPUanTFliY5rETHRMHRA==
+ dependencies:
+ uuid-js "^0.7.5"
+
+expo-font@~6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-6.0.1.tgz#239b0468edf90d441dca20253c00b334e812c5c5"
+ integrity sha512-zQwGFTKSrsTWmFzS0l87i6TyqM0YFDK4ui4sSzpbdQsUHXpeG7wfa67i09roLS0xtp85nrR9Vm2bUJp9njV8JQ==
+ dependencies:
+ fontfaceobserver "^2.1.0"
+
+expo-google-app-auth@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/expo-google-app-auth/-/expo-google-app-auth-6.0.0.tgz#1d9e4603271fa89d36ebaf604adb0169739f4235"
+ integrity sha512-23gkverd0OKGuCSjCxR/Uo08GvlMpTBR5yipbfwGosqvEk3WuCAhISGWIEg0Ozd0WA8DfZ9AyQhLYyWkbM4EIQ==
+ dependencies:
+ expo-app-auth "~6.0.0"
+ expo-constants "~6.0.0"
+
+expo-keep-awake@~6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-6.0.0.tgz#e0d6d1263c6a73488272a62aef98312ab25cab1f"
+ integrity sha512-MAtZknf6FtIC0ipkDS2FVa87al8YBsrpsQ2qMf+H/cI6FOd6aahaggN4x75xGnt5UzozgWfjhGNCi1XCr14rJw==
+
+expo-linear-gradient@^6.0.0, expo-linear-gradient@~6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/expo-linear-gradient/-/expo-linear-gradient-6.0.0.tgz#5fb0fb955dd22ef4ab032e543cb1c249885bf0b5"
+ integrity sha512-TGHSK7MsoU1wZXx9uEivMggAR/KT4wTSE0xBfhB8qsziGXoHZdoT79/tZ3HyWtCG7+JVUEFXfUOBxtOlZOu5tg==
+
+expo-localization@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/expo-localization/-/expo-localization-6.0.0.tgz#bbd6ee912ac6ba3a37e8ec334fbbeacb227e037f"
+ integrity sha512-ZU0621WDqmoMoko0p+lzGsLIsUTifHhsFqpf0w7S0fC+6djCBD47HFGzbm01sTVrRMS+tOsoVxa8D31ZaBqy5Q==
+ dependencies:
+ rtl-detect "^1.0.2"
+
+expo-location@~6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/expo-location/-/expo-location-6.0.0.tgz#da4e22ee5aa951d2c65d94f9916323eb4b3f8a01"
+ integrity sha512-5uSebmZos0DKJ/xpi+2e9myWVPUWk+fshFedi55wzlGqy2YpTG5MlDcCLlJlamgJ5Tm8+3ECdhbFX3g1pNRDVQ==
+ dependencies:
+ invariant "^2.2.4"
+
+expo-permissions@~6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/expo-permissions/-/expo-permissions-6.0.0.tgz#2943f1aa98de833b88cea73cf03d18d08957cb68"
+ integrity sha512-O+RdyfGiq7i+5Vi9fE38DgKn436lNWiqhnS5/Z7CC00bmKahhjVMNDbZvNn/nrdRGyaPneJk1Co1s1sexSnv0A==
+
+expo-sqlite@~6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/expo-sqlite/-/expo-sqlite-6.0.0.tgz#e7fe36b493a2230afdc77bdaedeab5f031690390"
+ integrity sha512-M8heovLeJoq7tb4f7PipDu0dqHSklbI2EqNvDM8XLjSZdSv6CqCMHg5Kvx0L9CLYTchjzktDPClZKjgvtGOVug==
+ dependencies:
+ "@expo/websql" "^1.0.1"
+ "@types/websql" "^0.0.27"
+ lodash "^4.17.11"
+
+expo-web-browser@~6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/expo-web-browser/-/expo-web-browser-6.0.0.tgz#63a59d4c02cd7ba47faa6a2eb04decb1a1ab2a32"
+ integrity sha512-7XkFPd4PRlVP6FscTnn78c0tY6+yLzb2Eai/ed+l+LB+hZWuhyY3ONzYM7/IKAiPmfhZr4Qs80vIa7iiavti8A==
+
+expo@^34.0.1:
+ version "34.0.4"
+ resolved "https://registry.yarnpkg.com/expo/-/expo-34.0.4.tgz#af6ef0da40c98981b5f3323c86fd815dfa417fff"
+ integrity sha512-sZQQoZnN5ASrkSA4qSsk7HPBewHB6b3k9VPZvchT0FBZ1fP5vpmzfIbVOqOLRXHf2VdjNnQVme617TnpPlruJg==
+ dependencies:
+ "@babel/runtime" "^7.1.2"
+ "@expo/vector-icons" "^10.0.2"
+ "@types/fbemitter" "^2.0.32"
+ "@types/invariant" "^2.2.29"
+ "@types/lodash.zipobject" "^4.1.4"
+ "@types/qs" "^6.5.1"
+ "@types/uuid-js" "^0.7.1"
+ "@unimodules/core" "~3.0.0"
+ "@unimodules/react-native-adapter" "~3.0.0"
+ babel-preset-expo "^6.0.0"
+ cross-spawn "^6.0.5"
+ expo-app-loader-provider "~6.0.0"
+ expo-asset "~6.0.0"
+ expo-constants "~6.0.0"
+ expo-file-system "~6.0.0"
+ expo-font "~6.0.1"
+ expo-keep-awake "~6.0.0"
+ expo-linear-gradient "~6.0.0"
+ expo-location "~6.0.0"
+ expo-permissions "~6.0.0"
+ expo-sqlite "~6.0.0"
+ expo-web-browser "~6.0.0"
+ fbemitter "^2.1.1"
+ invariant "^2.2.2"
+ lodash "^4.6.0"
+ md5-file "^3.2.3"
+ nullthrows "^1.1.0"
+ pretty-format "^23.6.0"
+ prop-types "^15.6.0"
+ qs "^6.5.0"
+ react-native-branch "~3.0.1"
+ react-native-view-shot "2.6.0"
+ serialize-error "^2.1.0"
+ unimodules-barcode-scanner-interface "~3.0.0"
+ unimodules-camera-interface "~3.0.0"
+ unimodules-constants-interface "~3.0.0"
+ unimodules-face-detector-interface "~3.0.0"
+ unimodules-file-system-interface "~3.0.0"
+ unimodules-font-interface "~3.0.0"
+ unimodules-image-loader-interface "~3.0.0"
+ unimodules-permissions-interface "~3.0.0"
+ unimodules-sensors-interface "~3.0.0"
+ unimodules-task-manager-interface "~3.0.0"
+ uuid-js "^0.7.5"
+
+extend-shallow@^1.1.2:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-1.1.4.tgz#19d6bf94dfc09d76ba711f39b872d21ff4dd9071"
+ integrity sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=
+ dependencies:
+ kind-of "^1.1.0"
+
+extend-shallow@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+ integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
+ dependencies:
+ is-extendable "^0.1.0"
+
+extend-shallow@^3.0.0, extend-shallow@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
+ integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
+ dependencies:
+ assign-symbols "^1.0.0"
+ is-extendable "^1.0.1"
+
+external-editor@^2.0.4:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5"
+ integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==
+ dependencies:
+ chardet "^0.4.0"
+ iconv-lite "^0.4.17"
+ tmp "^0.0.33"
+
+external-editor@^3.0.3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
+ integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
+ dependencies:
+ chardet "^0.7.0"
+ iconv-lite "^0.4.24"
+ tmp "^0.0.33"
+
+extglob@^0.3.1:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
+ integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=
+ dependencies:
+ is-extglob "^1.0.0"
+
+extglob@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
+ integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
+ dependencies:
+ array-unique "^0.3.2"
+ define-property "^1.0.0"
+ expand-brackets "^2.1.4"
+ extend-shallow "^2.0.1"
+ fragment-cache "^0.2.1"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.1"
+
+fancy-log@^1.3.2:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7"
+ integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==
+ dependencies:
+ ansi-gray "^0.1.1"
+ color-support "^1.1.3"
+ parse-node-version "^1.0.0"
+ time-stamp "^1.0.0"
+
+fast-deep-equal@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
+ integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
+
+fast-diff@^1.1.2:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
+ integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
+ integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
+
+fast-levenshtein@~2.0.4:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
+fb-watchman@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58"
+ integrity sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=
+ dependencies:
+ bser "^2.0.0"
+
+fbemitter@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-2.1.1.tgz#523e14fdaf5248805bb02f62efc33be703f51865"
+ integrity sha1-Uj4U/a9SSIBbsC9i78M75wP1GGU=
+ dependencies:
+ fbjs "^0.8.4"
+
+fbjs-css-vars@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8"
+ integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==
+
+fbjs-scripts@^1.0.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/fbjs-scripts/-/fbjs-scripts-1.2.0.tgz#069a0c0634242d10031c6460ef1fccefcdae8b27"
+ integrity sha512-5krZ8T0Bf8uky0abPoCLrfa7Orxd8UH4Qq8hRUF2RZYNMu+FmEOrBc7Ib3YVONmxTXTlLAvyrrdrVmksDb2OqQ==
+ dependencies:
+ "@babel/core" "^7.0.0"
+ ansi-colors "^1.0.1"
+ babel-preset-fbjs "^3.2.0"
+ core-js "^2.4.1"
+ cross-spawn "^5.1.0"
+ fancy-log "^1.3.2"
+ object-assign "^4.0.1"
+ plugin-error "^0.1.2"
+ semver "^5.1.0"
+ through2 "^2.0.0"
+
+fbjs@^0.8.4, fbjs@^0.8.9:
+ version "0.8.17"
+ resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
+ integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
+ dependencies:
+ core-js "^1.0.0"
+ isomorphic-fetch "^2.1.1"
+ loose-envify "^1.0.0"
+ object-assign "^4.1.0"
+ promise "^7.1.1"
+ setimmediate "^1.0.5"
+ ua-parser-js "^0.7.18"
+
+fbjs@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-1.0.0.tgz#52c215e0883a3c86af2a7a776ed51525ae8e0a5a"
+ integrity sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA==
+ dependencies:
+ core-js "^2.4.1"
+ fbjs-css-vars "^1.0.0"
+ isomorphic-fetch "^2.1.1"
+ loose-envify "^1.0.0"
+ object-assign "^4.1.0"
+ promise "^7.1.1"
+ setimmediate "^1.0.5"
+ ua-parser-js "^0.7.18"
+
+figures@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
+ integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
+ dependencies:
+ escape-string-regexp "^1.0.5"
+
+file-entry-cache@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c"
+ integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==
+ dependencies:
+ flat-cache "^2.0.1"
+
+filename-regex@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
+ integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=
+
+fill-range@^2.1.0:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565"
+ integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==
+ dependencies:
+ is-number "^2.1.0"
+ isobject "^2.0.0"
+ randomatic "^3.0.0"
+ repeat-element "^1.1.2"
+ repeat-string "^1.5.2"
+
+fill-range@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
+ integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
+ dependencies:
+ extend-shallow "^2.0.1"
+ is-number "^3.0.0"
+ repeat-string "^1.6.1"
+ to-regex-range "^2.1.0"
+
+finalhandler@1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
+ integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
+ dependencies:
+ 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"
+
+find-babel-config@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.2.0.tgz#a9b7b317eb5b9860cda9d54740a8c8337a2283a2"
+ integrity sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==
+ dependencies:
+ json5 "^0.5.1"
+ path-exists "^3.0.0"
+
+find-cache-dir@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
+ integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==
+ dependencies:
+ commondir "^1.0.1"
+ make-dir "^2.0.0"
+ pkg-dir "^3.0.0"
+
+find-up@^2.0.0, find-up@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
+ integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
+ dependencies:
+ locate-path "^2.0.0"
+
+find-up@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+ integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+ dependencies:
+ locate-path "^3.0.0"
+
+flat-cache@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0"
+ integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==
+ dependencies:
+ flatted "^2.0.0"
+ rimraf "2.6.3"
+ write "1.0.3"
+
+flatted@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08"
+ integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==
+
+fontfaceobserver@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fontfaceobserver/-/fontfaceobserver-2.1.0.tgz#e2705d293e2c585a6531c2a722905657317a2991"
+ integrity sha512-ReOsO2F66jUa0jmv2nlM/s1MiutJx/srhAe2+TE8dJCMi02ZZOcCTxTCQFr3Yet+uODUtnr4Mewg+tNQ+4V1Ng==
+
+for-in@^1.0.1, for-in@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+ integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
+
+for-own@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
+ integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=
+ dependencies:
+ for-in "^1.0.1"
+
+fragment-cache@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
+ integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
+ dependencies:
+ map-cache "^0.2.2"
+
+fresh@0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+ integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
+
+fs-extra@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950"
+ integrity sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=
+ dependencies:
+ graceful-fs "^4.1.2"
+ jsonfile "^2.1.0"
+ klaw "^1.0.0"
+
+fs-extra@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
+ integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
+ dependencies:
+ graceful-fs "^4.1.2"
+ jsonfile "^4.0.0"
+ universalify "^0.1.0"
+
+fs-minipass@^1.2.5:
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07"
+ integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==
+ dependencies:
+ minipass "^2.2.1"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+fsevents@^1.2.3:
+ version "1.2.9"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f"
+ integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==
+ dependencies:
+ nan "^2.12.1"
+ node-pre-gyp "^0.12.0"
+
+function-bind@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+functional-red-black-tree@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+ integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
+
+gauge@~1.2.5:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-1.2.7.tgz#e9cec5483d3d4ee0ef44b60a7d99e4935e136d93"
+ integrity sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=
+ dependencies:
+ ansi "^0.3.0"
+ has-unicode "^2.0.0"
+ lodash.pad "^4.1.0"
+ lodash.padend "^4.1.0"
+ lodash.padstart "^4.1.0"
+
+gauge@~2.7.3:
+ version "2.7.4"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
+ integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
+ dependencies:
+ aproba "^1.0.3"
+ console-control-strings "^1.0.0"
+ has-unicode "^2.0.0"
+ object-assign "^4.1.0"
+ signal-exit "^3.0.0"
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+ wide-align "^1.1.0"
+
+get-caller-file@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
+ integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
+
+get-caller-file@^2.0.1:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+ integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-stdin@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
+ integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
+
+get-stream@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
+ integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
+
+get-stream@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
+ integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+ dependencies:
+ pump "^3.0.0"
+
+get-value@^2.0.3, get-value@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
+ integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
+
+glob-base@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
+ integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=
+ dependencies:
+ glob-parent "^2.0.0"
+ is-glob "^2.0.0"
+
+glob-parent@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
+ integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=
+ dependencies:
+ is-glob "^2.0.0"
+
+glob-parent@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954"
+ integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==
+ dependencies:
+ is-glob "^4.0.1"
+
+glob@7.0.6:
+ version "7.0.6"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
+ integrity sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.2"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+glob@^7.1.1, glob@^7.1.2, glob@^7.1.3:
+ version "7.1.4"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
+ integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
+ dependencies:
+ 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"
+
+global@^4.3.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406"
+ integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==
+ dependencies:
+ min-document "^2.19.0"
+ process "^0.11.10"
+
+globals@^11.1.0, globals@^11.7.0:
+ version "11.12.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+ integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02"
+ integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==
+
+growly@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
+ integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
+
+hammerjs@^2.0.8:
+ version "2.0.8"
+ resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1"
+ integrity sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=
+
+has-ansi@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+ integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
+ dependencies:
+ ansi-regex "^2.0.0"
+
+has-flag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+ integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
+has-symbols@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
+ integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=
+
+has-unicode@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+ integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
+
+has-value@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
+ integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
+ dependencies:
+ get-value "^2.0.3"
+ has-values "^0.1.4"
+ isobject "^2.0.0"
+
+has-value@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
+ integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
+ dependencies:
+ get-value "^2.0.6"
+ has-values "^1.0.0"
+ isobject "^3.0.0"
+
+has-values@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
+ integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
+
+has-values@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
+ integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
+ dependencies:
+ is-number "^3.0.0"
+ kind-of "^4.0.0"
+
+hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0:
+ version "2.5.5"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
+ integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==
+
+hoist-non-react-statics@^3.0.1, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.2.1, hoist-non-react-statics@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
+ integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==
+ dependencies:
+ react-is "^16.7.0"
+
+hosted-git-info@^2.1.4:
+ version "2.8.4"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546"
+ integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==
+
+http-errors@~1.7.2:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
+ integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
+ dependencies:
+ depd "~1.1.2"
+ inherits "2.0.4"
+ setprototypeof "1.1.1"
+ statuses ">= 1.5.0 < 2"
+ toidentifier "1.0.0"
+
+hyphenate-style-name@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48"
+ integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ==
+
+i18n-js@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/i18n-js/-/i18n-js-3.3.0.tgz#05512f7184b5117c087ab597be649720a834c068"
+ integrity sha512-+m8jh84IIWlFwEJgwrWCkeIwIES9ilJKBOj5qx8ZTLLmlPz7bjKnCdxf254wRf6M4pkQHtgXGT9r9lGk0e9aug==
+
+iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+ integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
+ignore-walk@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8"
+ integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==
+ dependencies:
+ minimatch "^3.0.4"
+
+ignore@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
+ integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
+
+image-size@^0.6.0:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2"
+ integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA==
+
+immediate@^3.2.2:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c"
+ integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=
+
+immutable@3.8.2:
+ version "3.8.2"
+ resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
+ integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=
+
+import-fresh@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
+ integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY=
+ dependencies:
+ caller-path "^2.0.0"
+ resolve-from "^3.0.0"
+
+import-fresh@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118"
+ integrity sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+ integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+ini@~1.3.0:
+ version "1.3.5"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
+ integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
+
+inline-style-prefixer@^5.0.3:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-5.1.0.tgz#cb63195f9456dcda25cf59743e45c4d9815b0811"
+ integrity sha512-giteQHPMrApQOSjNSjteO5ZGSGMRf9gas14fRy2lg2buSc1nRnj6o6xuNds5cMTKrkncyrTu3gJn/yflFMVdmg==
+ dependencies:
+ css-in-js-utils "^2.0.0"
+
+inquirer@^3.0.6:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
+ integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==
+ dependencies:
+ ansi-escapes "^3.0.0"
+ chalk "^2.0.0"
+ cli-cursor "^2.1.0"
+ cli-width "^2.0.0"
+ external-editor "^2.0.4"
+ figures "^2.0.0"
+ lodash "^4.3.0"
+ mute-stream "0.0.7"
+ run-async "^2.2.0"
+ rx-lite "^4.0.8"
+ rx-lite-aggregates "^4.0.8"
+ string-width "^2.1.0"
+ strip-ansi "^4.0.0"
+ through "^2.3.6"
+
+inquirer@^6.4.1:
+ version "6.5.2"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca"
+ integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==
+ dependencies:
+ ansi-escapes "^3.2.0"
+ chalk "^2.4.2"
+ cli-cursor "^2.1.0"
+ cli-width "^2.0.0"
+ external-editor "^3.0.3"
+ figures "^2.0.0"
+ lodash "^4.17.12"
+ mute-stream "0.0.7"
+ run-async "^2.2.0"
+ rxjs "^6.4.0"
+ string-width "^2.1.0"
+ strip-ansi "^5.1.0"
+ through "^2.3.6"
+
+invariant@^2.2.2, invariant@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+ integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
+ dependencies:
+ loose-envify "^1.0.0"
+
+invert-kv@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
+ integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY=
+
+is-accessor-descriptor@^0.1.6:
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
+ integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
+ dependencies:
+ kind-of "^3.0.2"
+
+is-accessor-descriptor@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
+ integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
+ dependencies:
+ kind-of "^6.0.0"
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+ integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
+
+is-arrayish@^0.3.1:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
+ integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
+
+is-buffer@^1.1.5:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+ integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
+is-data-descriptor@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
+ integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
+ dependencies:
+ kind-of "^3.0.2"
+
+is-data-descriptor@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
+ integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
+ dependencies:
+ kind-of "^6.0.0"
+
+is-descriptor@^0.1.0:
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
+ integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
+ dependencies:
+ is-accessor-descriptor "^0.1.6"
+ is-data-descriptor "^0.1.4"
+ kind-of "^5.0.0"
+
+is-descriptor@^1.0.0, is-descriptor@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
+ integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
+ dependencies:
+ is-accessor-descriptor "^1.0.0"
+ is-data-descriptor "^1.0.0"
+ kind-of "^6.0.2"
+
+is-directory@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
+ integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=
+
+is-dotfile@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
+ integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=
+
+is-equal-shallow@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534"
+ integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=
+ dependencies:
+ is-primitive "^2.0.0"
+
+is-extendable@^0.1.0, is-extendable@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+ integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
+
+is-extendable@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
+ integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
+ dependencies:
+ is-plain-object "^2.0.4"
+
+is-extglob@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
+ integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-fullwidth-code-point@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+ integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+ integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+
+is-glob@^2.0.0, is-glob@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
+ integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=
+ dependencies:
+ is-extglob "^1.0.0"
+
+is-glob@^4.0.0, is-glob@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
+ integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-number@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
+ integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=
+ dependencies:
+ kind-of "^3.0.2"
+
+is-number@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+ integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
+ dependencies:
+ kind-of "^3.0.2"
+
+is-number@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
+ integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
+
+is-obj@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
+ integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
+
+is-plain-object@^2.0.3, is-plain-object@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+ integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+ dependencies:
+ isobject "^3.0.1"
+
+is-posix-bracket@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
+ integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=
+
+is-primitive@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
+ integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU=
+
+is-promise@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+ integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
+
+is-stream@^1.0.1, is-stream@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+ integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
+
+is-windows@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+ integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
+
+is-wsl@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
+ integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
+
+isarray@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+ integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
+
+isarray@1.0.0, isarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+ integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+isobject@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+ integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
+ dependencies:
+ isarray "1.0.0"
+
+isobject@^3.0.0, isobject@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+ integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+
+isomorphic-fetch@^2.1.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
+ integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
+ dependencies:
+ node-fetch "^1.0.1"
+ whatwg-fetch ">=0.10.0"
+
+jest-haste-map@24.0.0-alpha.6:
+ version "24.0.0-alpha.6"
+ resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.0.0-alpha.6.tgz#fb2c785080f391b923db51846b86840d0d773076"
+ integrity sha512-+NO2HMbjvrG8BC39ieLukdpFrcPhhjCJGhpbHodHNZygH1Tt06WrlNYGpZtWKx/zpf533tCtMQXO/q59JenjNw==
+ dependencies:
+ fb-watchman "^2.0.0"
+ graceful-fs "^4.1.11"
+ invariant "^2.2.4"
+ jest-serializer "^24.0.0-alpha.6"
+ jest-worker "^24.0.0-alpha.6"
+ micromatch "^2.3.11"
+ sane "^3.0.0"
+
+jest-serializer@24.0.0-alpha.6:
+ version "24.0.0-alpha.6"
+ resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.0.0-alpha.6.tgz#27d2fee4b1a85698717a30c3ec2ab80767312597"
+ integrity sha512-IPA5T6/GhlE6dedSk7Cd7YfuORnYjN0VD5iJVFn1Q81RJjpj++Hen5kJbKcg547vXsQ1TddV15qOA/zeIfOCLw==
+
+jest-serializer@^24.0.0-alpha.6:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73"
+ integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==
+
+jest-worker@24.0.0-alpha.6:
+ version "24.0.0-alpha.6"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.0.0-alpha.6.tgz#463681b92c117c57107135c14b9b9d6cd51d80ce"
+ integrity sha512-iXtH7MR9bjWlNnlnRBcrBRrb4cSVxML96La5vsnmBvDI+mJnkP5uEt6Fgpo5Y8f3z9y2Rd7wuPnKRxqQsiU/dA==
+ dependencies:
+ merge-stream "^1.0.1"
+
+jest-worker@^24.0.0-alpha.6:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5"
+ integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==
+ dependencies:
+ merge-stream "^2.0.0"
+ supports-color "^6.1.0"
+
+js-levenshtein@^1.1.3:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
+ integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^3.13.1:
+ version "3.13.1"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
+ integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
+jsesc@^2.5.1:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+ integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+jsesc@~0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+ integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
+
+json-parse-better-errors@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
+ integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
+
+json-stable-stringify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
+ integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=
+ dependencies:
+ jsonify "~0.0.0"
+
+json5@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
+ integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
+
+json5@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850"
+ integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==
+ dependencies:
+ minimist "^1.2.0"
+
+jsonfile@^2.1.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
+ integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug=
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+jsonfile@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+ integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+jsonify@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+ integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
+
+kind-of@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44"
+ integrity sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=
+
+kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+ integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
+ dependencies:
+ is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+ integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
+ dependencies:
+ is-buffer "^1.1.5"
+
+kind-of@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
+ integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
+
+kind-of@^6.0.0, kind-of@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
+ integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
+
+klaw@^1.0.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439"
+ integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk=
+ optionalDependencies:
+ graceful-fs "^4.1.9"
+
+lcid@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
+ integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=
+ dependencies:
+ invert-kv "^1.0.0"
+
+levn@^0.3.0, levn@~0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+ integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
+ dependencies:
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+
+load-json-file@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
+ integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=
+ dependencies:
+ graceful-fs "^4.1.2"
+ parse-json "^2.2.0"
+ pify "^2.0.0"
+ strip-bom "^3.0.0"
+
+locate-path@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
+ integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=
+ dependencies:
+ p-locate "^2.0.0"
+ path-exists "^3.0.0"
+
+locate-path@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+ integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+ dependencies:
+ p-locate "^3.0.0"
+ path-exists "^3.0.0"
+
+lodash-es@^4.17.15:
+ version "4.17.15"
+ resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
+ integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==
+
+lodash.get@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+ integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
+
+lodash.isequal@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
+ integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
+
+lodash.isfunction@^3.0.8:
+ version "3.0.9"
+ resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
+ integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==
+
+lodash.isobject@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d"
+ integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=
+
+lodash.merge@^4.3.1:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+ integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+lodash.pad@^4.1.0:
+ version "4.5.1"
+ resolved "https://registry.yarnpkg.com/lodash.pad/-/lodash.pad-4.5.1.tgz#4330949a833a7c8da22cc20f6a26c4d59debba70"
+ integrity sha1-QzCUmoM6fI2iLMIPaibE1Z3runA=
+
+lodash.padend@^4.1.0:
+ version "4.6.1"
+ resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e"
+ integrity sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=
+
+lodash.padstart@^4.1.0:
+ version "4.6.1"
+ resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b"
+ integrity sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=
+
+lodash.throttle@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
+ integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=
+
+lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.6.0, lodash@^4.6.1:
+ version "4.17.15"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
+ integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+ integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+ dependencies:
+ js-tokens "^3.0.0 || ^4.0.0"
+
+lru-cache@^4.0.1:
+ version "4.1.5"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
+ integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
+ dependencies:
+ pseudomap "^1.0.2"
+ yallist "^2.1.2"
+
+make-dir@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
+ integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
+ dependencies:
+ pify "^4.0.1"
+ semver "^5.6.0"
+
+makeerror@1.0.x:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c"
+ integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=
+ dependencies:
+ tmpl "1.0.x"
+
+map-cache@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
+ integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
+
+map-visit@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
+ integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
+ dependencies:
+ object-visit "^1.0.0"
+
+math-random@^1.0.1:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
+ integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==
+
+md5-file@^3.2.3:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/md5-file/-/md5-file-3.2.3.tgz#f9bceb941eca2214a4c0727f5e700314e770f06f"
+ integrity sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw==
+ dependencies:
+ buffer-alloc "^1.1.0"
+
+mem@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
+ integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=
+ dependencies:
+ mimic-fn "^1.0.0"
+
+merge-stream@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1"
+ integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=
+ dependencies:
+ readable-stream "^2.0.1"
+
+merge-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+ integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
+merge@^1.2.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145"
+ integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==
+
+metro-babel-register@0.51.0:
+ version "0.51.0"
+ resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.51.0.tgz#d86d3f2d90b45c7a3c6ae67a53bd1e50bad7a24d"
+ integrity sha512-rhdvHFOZ7/ub019A3+aYs8YeLydb02/FAMsKr2Nz2Jlf6VUxWrMnrcT0NYX16F9TGdi2ulRlJ9dwvUmdhkk+Bw==
+ dependencies:
+ "@babel/core" "^7.0.0"
+ "@babel/plugin-proposal-class-properties" "^7.0.0"
+ "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0"
+ "@babel/plugin-proposal-object-rest-spread" "^7.0.0"
+ "@babel/plugin-proposal-optional-catch-binding" "^7.0.0"
+ "@babel/plugin-proposal-optional-chaining" "^7.0.0"
+ "@babel/plugin-transform-async-to-generator" "^7.0.0"
+ "@babel/plugin-transform-flow-strip-types" "^7.0.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.0.0"
+ "@babel/register" "^7.0.0"
+ core-js "^2.2.2"
+ escape-string-regexp "^1.0.5"
+
+metro-babel-transformer@0.51.0:
+ version "0.51.0"
+ resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.51.0.tgz#9ee5199163ac46b2057527b3f8cbd8b089ffc03e"
+ integrity sha512-M7KEY/hjD3E8tJEliWgI0VOSaJtqaznC0ItM6FiMrhoGDqqa1BvGofl+EPcKqjBSOV1UgExua/T1VOIWbjwQsw==
+ dependencies:
+ "@babel/core" "^7.0.0"
+
+metro-babel-transformer@0.51.1:
+ version "0.51.1"
+ resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.51.1.tgz#97be9e2b96c78aa202b52ae05fb86f71327aef72"
+ integrity sha512-+tOnZZzOzufB86ASdfimUEGB1jBKsdsVpPdjNJZkueTFyvYlGqWDQKHM1w9bwKMeM/czPQ48Y6m8Bou6le0X4w==
+ dependencies:
+ "@babel/core" "^7.0.0"
+
+metro-babel7-plugin-react-transform@0.51.0:
+ version "0.51.0"
+ resolved "https://registry.yarnpkg.com/metro-babel7-plugin-react-transform/-/metro-babel7-plugin-react-transform-0.51.0.tgz#af27dd81666b91f05d2b371b0d6d283c585e38b6"
+ integrity sha512-dZ95kXcE2FJMoRsYhxr7YLCbOlHWKwe0bOpihRhfImDTgFfuKIzU4ROQwMUbE0NCbzB+ATFsa2FZ3pHDJ5GI0w==
+ dependencies:
+ "@babel/helper-module-imports" "^7.0.0"
+
+metro-babel7-plugin-react-transform@0.51.1:
+ version "0.51.1"
+ resolved "https://registry.yarnpkg.com/metro-babel7-plugin-react-transform/-/metro-babel7-plugin-react-transform-0.51.1.tgz#9cce2c340cc4006fc82aa6dfab27af22d592607e"
+ integrity sha512-wzn4X9KgmAMZ7Bi6v9KxA7dw+AHGL0RODPxU5NDJ3A6d0yERvzfZ3qkzWhz8jbFkVBK12cu5DTho3HBazKQDOw==
+ dependencies:
+ "@babel/helper-module-imports" "^7.0.0"
+
+metro-cache@0.51.1:
+ version "0.51.1"
+ resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.51.1.tgz#d0b296eab8e009214413bba87e4eac3d9b44cd04"
+ integrity sha512-0m1+aicsw77LVAehNuTxDpE1c/7Xv/ajRD+UL/lFCWUxnrjSbxVtIKr8l5DxEY11082c1axVRuaV9e436W+eXg==
+ dependencies:
+ jest-serializer "24.0.0-alpha.6"
+ metro-core "0.51.1"
+ mkdirp "^0.5.1"
+ rimraf "^2.5.4"
+
+metro-config@0.51.1, metro-config@^0.51.0:
+ version "0.51.1"
+ resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.51.1.tgz#8f1a241ce2c0b521cd492c39bc5c6c69e3397b82"
+ integrity sha512-WCNd0tTI9gb/ubgTqK1+ljZL4b3hsXVinsOAtep4nHiVb6DSDdbO2yXDD2rpYx3NE6hDRMFS9HHg6G0139pAqQ==
+ dependencies:
+ cosmiconfig "^5.0.5"
+ metro "0.51.1"
+ metro-cache "0.51.1"
+ metro-core "0.51.1"
+ pretty-format "24.0.0-alpha.6"
+
+metro-core@0.51.1, metro-core@^0.51.0:
+ version "0.51.1"
+ resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.51.1.tgz#e7227fb1dd1bb3f953272fad9876e6201140b038"
+ integrity sha512-sG1yACjdFqmIzZN50HqLTKUMp1oy0AehHhmIuYeIllo1DjX6Y2o3UAT3rGP8U+SAqJGXf/OWzl6VNyRPGDENfA==
+ dependencies:
+ jest-haste-map "24.0.0-alpha.6"
+ lodash.throttle "^4.1.1"
+ metro-resolver "0.51.1"
+ wordwrap "^1.0.0"
+
+metro-memory-fs@^0.51.0:
+ version "0.51.1"
+ resolved "https://registry.yarnpkg.com/metro-memory-fs/-/metro-memory-fs-0.51.1.tgz#624291f5956b0fd11532d80b1b85d550926f96c9"
+ integrity sha512-dXVUpLPLwfQcYHd1HlqHGVzBsiwvUdT92TDSbdc10152TP+iynHBqLDWbxt0MAtd6c/QXwOuGZZ1IcX3+lv5iw==
+
+metro-minify-uglify@0.51.1:
+ version "0.51.1"
+ resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.51.1.tgz#60cd8fe4d3e82d6670c717b8ddb52ae63199c0e4"
+ integrity sha512-HAqd/rFrQ6mnbqVAszDXIKTg2rqHlY9Fm8DReakgbkAeyMbF2mH3kEgtesPmTrhajdFk81UZcNSm6wxj1JMgVg==
+ dependencies:
+ uglify-es "^3.1.9"
+
+metro-react-native-babel-preset@0.51.0:
+ version "0.51.0"
+ resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.51.0.tgz#978d960acf2d214bbbe43e59145878d663bd07de"
+ integrity sha512-Y/aPeLl4RzY8IEAneOyDcpdjto/8yjIuX9eUWRngjSqdHYhGQtqiSBpfTpo0BvXpwNRLwCLHyXo58gNpckTJFw==
+ dependencies:
+ "@babel/plugin-proposal-class-properties" "^7.0.0"
+ "@babel/plugin-proposal-export-default-from" "^7.0.0"
+ "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0"
+ "@babel/plugin-proposal-object-rest-spread" "^7.0.0"
+ "@babel/plugin-proposal-optional-catch-binding" "^7.0.0"
+ "@babel/plugin-proposal-optional-chaining" "^7.0.0"
+ "@babel/plugin-syntax-dynamic-import" "^7.0.0"
+ "@babel/plugin-syntax-export-default-from" "^7.0.0"
+ "@babel/plugin-transform-arrow-functions" "^7.0.0"
+ "@babel/plugin-transform-block-scoping" "^7.0.0"
+ "@babel/plugin-transform-classes" "^7.0.0"
+ "@babel/plugin-transform-computed-properties" "^7.0.0"
+ "@babel/plugin-transform-destructuring" "^7.0.0"
+ "@babel/plugin-transform-exponentiation-operator" "^7.0.0"
+ "@babel/plugin-transform-flow-strip-types" "^7.0.0"
+ "@babel/plugin-transform-for-of" "^7.0.0"
+ "@babel/plugin-transform-function-name" "^7.0.0"
+ "@babel/plugin-transform-literals" "^7.0.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.0.0"
+ "@babel/plugin-transform-object-assign" "^7.0.0"
+ "@babel/plugin-transform-parameters" "^7.0.0"
+ "@babel/plugin-transform-react-display-name" "^7.0.0"
+ "@babel/plugin-transform-react-jsx" "^7.0.0"
+ "@babel/plugin-transform-react-jsx-source" "^7.0.0"
+ "@babel/plugin-transform-regenerator" "^7.0.0"
+ "@babel/plugin-transform-runtime" "^7.0.0"
+ "@babel/plugin-transform-shorthand-properties" "^7.0.0"
+ "@babel/plugin-transform-spread" "^7.0.0"
+ "@babel/plugin-transform-sticky-regex" "^7.0.0"
+ "@babel/plugin-transform-template-literals" "^7.0.0"
+ "@babel/plugin-transform-typescript" "^7.0.0"
+ "@babel/plugin-transform-unicode-regex" "^7.0.0"
+ "@babel/template" "^7.0.0"
+ metro-babel7-plugin-react-transform "0.51.0"
+ react-transform-hmr "^1.0.4"
+
+metro-react-native-babel-preset@0.51.1, metro-react-native-babel-preset@^0.51.1:
+ version "0.51.1"
+ resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.51.1.tgz#44aeeedfea37f7c2ab8f6f273fa71b90fe65f089"
+ integrity sha512-e9tsYDFhU70gar0jQWcZXRPJVCv4k7tEs6Pm74wXO2OO/T1MEumbvniDIGwGG8bG8RUnYdHhjcaiub2Vc5BRWw==
+ dependencies:
+ "@babel/plugin-proposal-class-properties" "^7.0.0"
+ "@babel/plugin-proposal-export-default-from" "^7.0.0"
+ "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0"
+ "@babel/plugin-proposal-object-rest-spread" "^7.0.0"
+ "@babel/plugin-proposal-optional-catch-binding" "^7.0.0"
+ "@babel/plugin-proposal-optional-chaining" "^7.0.0"
+ "@babel/plugin-syntax-dynamic-import" "^7.0.0"
+ "@babel/plugin-syntax-export-default-from" "^7.0.0"
+ "@babel/plugin-transform-arrow-functions" "^7.0.0"
+ "@babel/plugin-transform-block-scoping" "^7.0.0"
+ "@babel/plugin-transform-classes" "^7.0.0"
+ "@babel/plugin-transform-computed-properties" "^7.0.0"
+ "@babel/plugin-transform-destructuring" "^7.0.0"
+ "@babel/plugin-transform-exponentiation-operator" "^7.0.0"
+ "@babel/plugin-transform-flow-strip-types" "^7.0.0"
+ "@babel/plugin-transform-for-of" "^7.0.0"
+ "@babel/plugin-transform-function-name" "^7.0.0"
+ "@babel/plugin-transform-literals" "^7.0.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.0.0"
+ "@babel/plugin-transform-object-assign" "^7.0.0"
+ "@babel/plugin-transform-parameters" "^7.0.0"
+ "@babel/plugin-transform-react-display-name" "^7.0.0"
+ "@babel/plugin-transform-react-jsx" "^7.0.0"
+ "@babel/plugin-transform-react-jsx-source" "^7.0.0"
+ "@babel/plugin-transform-regenerator" "^7.0.0"
+ "@babel/plugin-transform-runtime" "^7.0.0"
+ "@babel/plugin-transform-shorthand-properties" "^7.0.0"
+ "@babel/plugin-transform-spread" "^7.0.0"
+ "@babel/plugin-transform-sticky-regex" "^7.0.0"
+ "@babel/plugin-transform-template-literals" "^7.0.0"
+ "@babel/plugin-transform-typescript" "^7.0.0"
+ "@babel/plugin-transform-unicode-regex" "^7.0.0"
+ "@babel/template" "^7.0.0"
+ metro-babel7-plugin-react-transform "0.51.1"
+ react-transform-hmr "^1.0.4"
+
+metro-react-native-babel-transformer@0.51.0:
+ version "0.51.0"
+ resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.51.0.tgz#57a695e97a19d95de63c9633f9d0dc024ee8e99a"
+ integrity sha512-VFnqtE0qrVmU1HV9B04o53+NZHvDwR+CWCoEx4+7vCqJ9Tvas741biqCjah9xtifoKdElQELk6x0soOAWCDFJA==
+ dependencies:
+ "@babel/core" "^7.0.0"
+ babel-preset-fbjs "^3.0.1"
+ metro-babel-transformer "0.51.0"
+ metro-react-native-babel-preset "0.51.0"
+
+metro-react-native-babel-transformer@^0.51.0:
+ version "0.51.1"
+ resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.51.1.tgz#bac34f988c150c725cd1875c13701cc2032615f9"
+ integrity sha512-D0KU+JPb/Z76nUWt3+bkjKggOlGvqAVI2BpIH2JFKprpUyBjWaCRqHnkBfZGixYwUfmu93MIlKJWr6iKzzFrlg==
+ dependencies:
+ "@babel/core" "^7.0.0"
+ babel-preset-fbjs "^3.0.1"
+ metro-babel-transformer "0.51.1"
+ metro-react-native-babel-preset "0.51.1"
+
+metro-resolver@0.51.1:
+ version "0.51.1"
+ resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.51.1.tgz#4c26f0baee47d30250187adca3d34c902e627611"
+ integrity sha512-zmWbD/287NDA/jLPuPV0hne/YMMSG0dljzu21TYMg2lXRLur/zROJHHhyepZvuBHgInXBi4Vhr2wvuSnY39SuA==
+ dependencies:
+ absolute-path "^0.0.0"
+
+metro-source-map@0.51.1:
+ version "0.51.1"
+ resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.51.1.tgz#1a8da138e98e184304d5558b4f92a5c2141822d0"
+ integrity sha512-JyrE+RV4YumrboHPHTGsUUGERjQ681ImRLrSYDGcmNv4tfpk9nvAK26UAas4IvBYFCC9oW90m0udt3kaQGv59Q==
+ dependencies:
+ source-map "^0.5.6"
+
+metro@0.51.1, metro@^0.51.0:
+ version "0.51.1"
+ resolved "https://registry.yarnpkg.com/metro/-/metro-0.51.1.tgz#b0aad4731593b9f244261bad1abb2a006d1c8969"
+ integrity sha512-nM0dqn8LQlMjhChl2fzTUq2EWiUebZM7nkesD9vQe47W10bj/tbRLPiIIAxht6SRDbPd/hRA+t39PxLhPSKEKg==
+ dependencies:
+ "@babel/core" "^7.0.0"
+ "@babel/generator" "^7.0.0"
+ "@babel/parser" "^7.0.0"
+ "@babel/plugin-external-helpers" "^7.0.0"
+ "@babel/template" "^7.0.0"
+ "@babel/traverse" "^7.0.0"
+ "@babel/types" "^7.0.0"
+ absolute-path "^0.0.0"
+ async "^2.4.0"
+ babel-preset-fbjs "^3.0.1"
+ buffer-crc32 "^0.2.13"
+ chalk "^2.4.1"
+ concat-stream "^1.6.0"
+ connect "^3.6.5"
+ debug "^2.2.0"
+ denodeify "^1.2.1"
+ eventemitter3 "^3.0.0"
+ fbjs "^1.0.0"
+ fs-extra "^1.0.0"
+ graceful-fs "^4.1.3"
+ image-size "^0.6.0"
+ invariant "^2.2.4"
+ jest-haste-map "24.0.0-alpha.6"
+ jest-worker "24.0.0-alpha.6"
+ json-stable-stringify "^1.0.1"
+ lodash.throttle "^4.1.1"
+ merge-stream "^1.0.1"
+ metro-babel-transformer "0.51.1"
+ metro-cache "0.51.1"
+ metro-config "0.51.1"
+ metro-core "0.51.1"
+ metro-minify-uglify "0.51.1"
+ metro-react-native-babel-preset "0.51.1"
+ metro-resolver "0.51.1"
+ metro-source-map "0.51.1"
+ mime-types "2.1.11"
+ mkdirp "^0.5.1"
+ node-fetch "^2.2.0"
+ nullthrows "^1.1.0"
+ react-transform-hmr "^1.0.4"
+ resolve "^1.5.0"
+ rimraf "^2.5.4"
+ serialize-error "^2.1.0"
+ source-map "^0.5.6"
+ temp "0.8.3"
+ throat "^4.1.0"
+ wordwrap "^1.0.0"
+ write-file-atomic "^1.2.0"
+ ws "^1.1.5"
+ xpipe "^1.0.5"
+ yargs "^9.0.0"
+
+micromatch@^2.3.11:
+ version "2.3.11"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
+ integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=
+ dependencies:
+ arr-diff "^2.0.0"
+ array-unique "^0.2.1"
+ braces "^1.8.2"
+ expand-brackets "^0.1.4"
+ extglob "^0.3.1"
+ filename-regex "^2.0.0"
+ is-extglob "^1.0.0"
+ is-glob "^2.0.1"
+ kind-of "^3.0.2"
+ normalize-path "^2.0.1"
+ object.omit "^2.0.0"
+ parse-glob "^3.0.4"
+ regex-cache "^0.4.2"
+
+micromatch@^3.1.4:
+ version "3.1.10"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
+ integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
+ dependencies:
+ arr-diff "^4.0.0"
+ array-unique "^0.3.2"
+ braces "^2.3.1"
+ define-property "^2.0.2"
+ extend-shallow "^3.0.2"
+ extglob "^2.0.4"
+ fragment-cache "^0.2.1"
+ kind-of "^6.0.2"
+ nanomatch "^1.2.9"
+ object.pick "^1.3.0"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.2"
+
+mime-db@1.40.0, "mime-db@>= 1.40.0 < 2":
+ version "1.40.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
+ integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==
+
+mime-db@~1.23.0:
+ version "1.23.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.23.0.tgz#a31b4070adaea27d732ea333740a64d0ec9a6659"
+ integrity sha1-oxtAcK2uon1zLqMzdApk0OyaZlk=
+
+mime-types@2.1.11:
+ version "2.1.11"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.11.tgz#c259c471bda808a85d6cd193b430a5fae4473b3c"
+ integrity sha1-wlnEcb2oCKhdbNGTtDCl+uRHOzw=
+ dependencies:
+ mime-db "~1.23.0"
+
+mime-types@~2.1.24:
+ version "2.1.24"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81"
+ integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
+ dependencies:
+ mime-db "1.40.0"
+
+mime@1.6.0, mime@^1.3.4:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+ integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+
+mimic-fn@^1.0.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
+ integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
+
+min-document@^2.19.0:
+ version "2.19.0"
+ resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
+ integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=
+ dependencies:
+ dom-walk "^0.1.0"
+
+minimatch@^3.0.2, minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+ integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
+
+minimist@^1.1.1, minimist@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+ integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
+
+minimist@~0.0.1:
+ version "0.0.10"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
+ integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
+
+minipass@^2.2.1, minipass@^2.3.5:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.4.0.tgz#38f0af94f42fb6f34d3d7d82a90e2c99cd3ff485"
+ integrity sha512-6PmOuSP4NnZXzs2z6rbwzLJu/c5gdzYg1mRI/WIYdx45iiX7T+a4esOzavD6V/KmBzAaopFSTZPZcUx73bqKWA==
+ dependencies:
+ safe-buffer "^5.1.2"
+ yallist "^3.0.0"
+
+minizlib@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614"
+ integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==
+ dependencies:
+ minipass "^2.2.1"
+
+mixin-deep@^1.2.0:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
+ integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==
+ dependencies:
+ for-in "^1.0.2"
+ is-extendable "^1.0.1"
+
+mkdirp@^0.5.0, mkdirp@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+ integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
+ dependencies:
+ minimist "0.0.8"
+
+moment@^2.24.0:
+ version "2.24.0"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
+ integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
+
+morgan@^1.9.0:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.1.tgz#0a8d16734a1d9afbc824b99df87e738e58e2da59"
+ integrity sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==
+ dependencies:
+ basic-auth "~2.0.0"
+ debug "2.6.9"
+ depd "~1.1.2"
+ on-finished "~2.3.0"
+ on-headers "~1.0.1"
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
+ integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
+
+ms@^2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+mute-stream@0.0.7:
+ version "0.0.7"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
+ integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
+
+nan@^2.12.1:
+ version "2.14.0"
+ resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
+ integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
+
+nanomatch@^1.2.9:
+ version "1.2.13"
+ resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
+ integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
+ dependencies:
+ arr-diff "^4.0.0"
+ array-unique "^0.3.2"
+ define-property "^2.0.2"
+ extend-shallow "^3.0.2"
+ fragment-cache "^0.2.1"
+ is-windows "^1.0.2"
+ kind-of "^6.0.2"
+ object.pick "^1.3.0"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.1"
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+
+needle@^2.2.1:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c"
+ integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==
+ dependencies:
+ debug "^3.2.6"
+ iconv-lite "^0.4.4"
+ sax "^1.2.4"
+
+negotiator@0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
+ integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
+
+nice-try@^1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
+ integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+
+node-fetch@^1.0.1:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
+ integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
+ dependencies:
+ encoding "^0.1.11"
+ is-stream "^1.0.1"
+
+node-fetch@^2.2.0:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
+ integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
+
+node-int64@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
+ integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=
+
+node-modules-regexp@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40"
+ integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=
+
+node-notifier@^5.2.1:
+ version "5.4.3"
+ resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50"
+ integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==
+ dependencies:
+ growly "^1.3.0"
+ is-wsl "^1.1.0"
+ semver "^5.5.0"
+ shellwords "^0.1.1"
+ which "^1.3.0"
+
+node-pre-gyp@^0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149"
+ integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==
+ dependencies:
+ detect-libc "^1.0.2"
+ mkdirp "^0.5.1"
+ needle "^2.2.1"
+ nopt "^4.0.1"
+ npm-packlist "^1.1.6"
+ npmlog "^4.0.2"
+ rc "^1.2.7"
+ rimraf "^2.6.1"
+ semver "^5.3.0"
+ tar "^4"
+
+node-releases@^1.1.25:
+ version "1.1.28"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.28.tgz#503c3c70d0e4732b84e7aaa2925fbdde10482d4a"
+ integrity sha512-AQw4emh6iSXnCpDiFe0phYcThiccmkNWMZnFZ+lDJjAP8J0m2fVd59duvUUyuTirQOhIAajTFkzG6FHCLBO59g==
+ dependencies:
+ semver "^5.3.0"
+
+noop-fn@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/noop-fn/-/noop-fn-1.0.0.tgz#5f33d47f13d2150df93e0cb036699e982f78ffbf"
+ integrity sha1-XzPUfxPSFQ35PgywNmmemC94/78=
+
+nopt@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
+ integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
+ dependencies:
+ abbrev "1"
+ osenv "^0.1.4"
+
+normalize-css-color@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/normalize-css-color/-/normalize-css-color-1.0.2.tgz#02991e97cccec6623fe573afbbf0de6a1f3e9f8d"
+ integrity sha1-Apkel8zOxmI/5XOvu/Deah8+n40=
+
+normalize-package-data@^2.3.2:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
+ integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
+ dependencies:
+ hosted-git-info "^2.1.4"
+ resolve "^1.10.0"
+ semver "2 || 3 || 4 || 5"
+ validate-npm-package-license "^3.0.1"
+
+normalize-path@^2.0.1, normalize-path@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
+ integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
+ dependencies:
+ remove-trailing-separator "^1.0.1"
+
+npm-bundled@^1.0.1:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd"
+ integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==
+
+npm-packlist@^1.1.6:
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44"
+ integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==
+ dependencies:
+ ignore-walk "^3.0.1"
+ npm-bundled "^1.0.1"
+
+npm-run-path@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
+ integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
+ dependencies:
+ path-key "^2.0.0"
+
+npmlog@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-2.0.4.tgz#98b52530f2514ca90d09ec5b22c8846722375692"
+ integrity sha1-mLUlMPJRTKkNCexbIsiEZyI3VpI=
+ dependencies:
+ ansi "~0.3.1"
+ are-we-there-yet "~1.1.2"
+ gauge "~1.2.5"
+
+npmlog@^4.0.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
+ integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
+ dependencies:
+ are-we-there-yet "~1.1.2"
+ console-control-strings "~1.1.0"
+ gauge "~2.7.3"
+ set-blocking "~2.0.0"
+
+nullthrows@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
+ integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==
+
+number-is-nan@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+ integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
+
+object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+object-copy@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
+ integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
+ dependencies:
+ copy-descriptor "^0.1.0"
+ define-property "^0.2.5"
+ kind-of "^3.0.3"
+
+object-keys@^1.0.11, object-keys@^1.0.12:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+ integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+object-visit@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
+ integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
+ dependencies:
+ isobject "^3.0.0"
+
+object.assign@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
+ integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
+ dependencies:
+ define-properties "^1.1.2"
+ function-bind "^1.1.1"
+ has-symbols "^1.0.0"
+ object-keys "^1.0.11"
+
+object.omit@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
+ integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=
+ dependencies:
+ for-own "^0.1.4"
+ is-extendable "^0.1.1"
+
+object.pick@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
+ integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
+ dependencies:
+ isobject "^3.0.1"
+
+on-finished@~2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+ integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
+ dependencies:
+ ee-first "1.1.1"
+
+on-headers@~1.0.1, on-headers@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
+ integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
+
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ dependencies:
+ wrappy "1"
+
+onetime@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+ integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
+ dependencies:
+ mimic-fn "^1.0.0"
+
+opencollective-postinstall@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89"
+ integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==
+
+opn@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/opn/-/opn-3.0.3.tgz#b6d99e7399f78d65c3baaffef1fb288e9b85243a"
+ integrity sha1-ttmec5n3jWXDuq/+8fsojpuFJDo=
+ dependencies:
+ object-assign "^4.0.1"
+
+optimist@^0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
+ integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY=
+ dependencies:
+ minimist "~0.0.1"
+ wordwrap "~0.0.2"
+
+optionator@^0.8.2:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
+ integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=
+ dependencies:
+ deep-is "~0.1.3"
+ fast-levenshtein "~2.0.4"
+ levn "~0.3.0"
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+ wordwrap "~1.0.0"
+
+options@>=0.0.5:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f"
+ integrity sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=
+
+os-homedir@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+ integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
+
+os-locale@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
+ integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==
+ dependencies:
+ execa "^0.7.0"
+ lcid "^1.0.0"
+ mem "^1.1.0"
+
+os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+ integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
+
+osenv@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
+ integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
+ dependencies:
+ os-homedir "^1.0.0"
+ os-tmpdir "^1.0.0"
+
+p-finally@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+ integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
+
+p-limit@^1.1.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
+ integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
+ dependencies:
+ p-try "^1.0.0"
+
+p-limit@^2.0.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537"
+ integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==
+ dependencies:
+ p-try "^2.0.0"
+
+p-locate@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
+ integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=
+ dependencies:
+ p-limit "^1.1.0"
+
+p-locate@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+ integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+ dependencies:
+ p-limit "^2.0.0"
+
+p-try@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
+ integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
+
+p-try@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
+parse-glob@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
+ integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw=
+ dependencies:
+ glob-base "^0.3.0"
+ is-dotfile "^1.0.0"
+ is-extglob "^1.0.0"
+ is-glob "^2.0.0"
+
+parse-json@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+ integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
+ dependencies:
+ error-ex "^1.2.0"
+
+parse-json@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+ integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
+ dependencies:
+ error-ex "^1.3.1"
+ json-parse-better-errors "^1.0.1"
+
+parse-node-version@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b"
+ integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==
+
+parseurl@~1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+ integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
+pascalcase@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
+ integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
+
+path-browserify@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.0.tgz#40702a97af46ae00b0ea6fa8998c0b03c0af160d"
+ integrity sha512-Hkavx/nY4/plImrZPHRk2CL9vpOymZLgEbMNX1U0bjcBL7QN9wODxyx0yaMZURSQaUtSEvDrfAvxa9oPb0at9g==
+
+path-exists@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+ integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-key@^2.0.0, path-key@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+ integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
+
+path-parse@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+ integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+
+path-to-regexp@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
+ integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=
+ dependencies:
+ isarray "0.0.1"
+
+path-type@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
+ integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=
+ dependencies:
+ pify "^2.0.0"
+
+pify@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+ integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
+
+pify@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
+ integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+
+pirates@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87"
+ integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==
+ dependencies:
+ node-modules-regexp "^1.0.0"
+
+pkg-dir@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
+ integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==
+ dependencies:
+ find-up "^3.0.0"
+
+pkg-up@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
+ integrity sha1-yBmscoBZpGHKscOImivjxJoATX8=
+ dependencies:
+ find-up "^2.1.0"
+
+plist@^3.0.0, plist@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c"
+ integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==
+ dependencies:
+ base64-js "^1.2.3"
+ xmlbuilder "^9.0.7"
+ xmldom "0.1.x"
+
+plugin-error@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace"
+ integrity sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=
+ dependencies:
+ ansi-cyan "^0.1.1"
+ ansi-red "^0.1.1"
+ arr-diff "^1.0.1"
+ arr-union "^2.0.1"
+ extend-shallow "^1.1.2"
+
+posix-character-classes@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
+ integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+
+pouchdb-collections@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/pouchdb-collections/-/pouchdb-collections-1.0.1.tgz#fe63a17da977611abef7cb8026cb1a9553fd8359"
+ integrity sha1-/mOhfal3YRq+98uAJssalVP9g1k=
+
+prelude-ls@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+ integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
+
+preserve@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
+ integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=
+
+prettier-linter-helpers@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
+ integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
+ dependencies:
+ fast-diff "^1.1.2"
+
+prettier@1.18.2:
+ version "1.18.2"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
+ integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
+
+pretty-format@24.0.0-alpha.6:
+ version "24.0.0-alpha.6"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.0.0-alpha.6.tgz#25ad2fa46b342d6278bf241c5d2114d4376fbac1"
+ integrity sha512-zG2m6YJeuzwBFqb5EIdmwYVf30sap+iMRuYNPytOccEXZMAJbPIFGKVJ/U0WjQegmnQbRo9CI7j6j3HtDaifiA==
+ dependencies:
+ ansi-regex "^4.0.0"
+ ansi-styles "^3.2.0"
+
+pretty-format@^23.6.0:
+ version "23.6.0"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760"
+ integrity sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==
+ dependencies:
+ ansi-regex "^3.0.0"
+ ansi-styles "^3.2.0"
+
+private@^0.1.6:
+ version "0.1.8"
+ resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
+ integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
+
+process-nextick-args@~2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+ integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+process@^0.11.10:
+ version "0.11.10"
+ resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+ integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
+
+progress@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
+ integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
+
+promise@^7.1.1:
+ version "7.3.1"
+ resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
+ integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
+ dependencies:
+ asap "~2.0.3"
+
+prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
+ version "15.7.2"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
+ integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.8.1"
+
+pseudomap@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+ integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
+
+pump@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
+ integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
+ dependencies:
+ end-of-stream "^1.1.0"
+ once "^1.3.1"
+
+punycode@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+qs@^6.5.0:
+ version "6.8.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.8.0.tgz#87b763f0d37ca54200334cd57bb2ef8f68a1d081"
+ integrity sha512-tPSkj8y92PfZVbinY1n84i1Qdx75lZjMQYx9WZhnkofyxzw2r7Ho39G3/aEvSUdebxpnnM4LZJCtvE/Aq3+s9w==
+
+query-string@^6.4.2:
+ version "6.8.3"
+ resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.8.3.tgz#fd9fb7ffb068b79062b43383685611ee47777d4b"
+ integrity sha512-llcxWccnyaWlODe7A9hRjkvdCKamEKTh+wH8ITdTc3OhchaqUZteiSCX/2ablWHVrkVIe04dntnaZJ7BdyW0lQ==
+ dependencies:
+ decode-uri-component "^0.2.0"
+ split-on-first "^1.0.0"
+ strict-uri-encode "^2.0.0"
+
+querystringify@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
+ integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
+
+randomatic@^3.0.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed"
+ integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==
+ dependencies:
+ is-number "^4.0.0"
+ kind-of "^6.0.0"
+ math-random "^1.0.1"
+
+range-parser@~1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+ integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
+
+rc@^1.2.7:
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
+ integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
+ dependencies:
+ deep-extend "^0.6.0"
+ ini "~1.3.0"
+ minimist "^1.2.0"
+ strip-json-comments "~2.0.1"
+
+react-clone-referenced-element@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/react-clone-referenced-element/-/react-clone-referenced-element-1.1.0.tgz#9cdda7f2aeb54fea791f3ab8c6ab96c7a77d0158"
+ integrity sha512-FKOsfKbBkPxYE8576EM6uAfHC4rnMpLyH6/TJUL4WcHUEB3EUn8AxPjnnV/IiwSSzsClvHYK+sDELKN/EJ0WYg==
+
+react-deep-force-update@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/react-deep-force-update/-/react-deep-force-update-1.1.2.tgz#3d2ae45c2c9040cbb1772be52f8ea1ade6ca2ee1"
+ integrity sha512-WUSQJ4P/wWcusaH+zZmbECOk7H5N2pOIl0vzheeornkIMhu+qrNdGFm0bDZLCb0hSF0jf/kH1SgkNGfBdTc4wA==
+
+react-devtools-core@^3.6.0:
+ version "3.6.3"
+ resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-3.6.3.tgz#977d95b684c6ad28205f0c62e1e12c5f16675814"
+ integrity sha512-+P+eFy/yo8Z/UH9J0DqHZuUM5+RI2wl249TNvMx3J2jpUomLQa4Zxl56GEotGfw3PIP1eI+hVf1s53FlUONStQ==
+ dependencies:
+ shell-quote "^1.6.1"
+ ws "^3.3.1"
+
+react-dom@^16.8.6:
+ version "16.9.0"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.9.0.tgz#5e65527a5e26f22ae3701131bcccaee9fb0d3962"
+ integrity sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==
+ dependencies:
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+ prop-types "^15.6.2"
+ scheduler "^0.15.0"
+
+react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6, react-is@^16.9.0:
+ version "16.9.0"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb"
+ integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==
+
+react-lifecycles-compat@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
+ integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
+
+react-native-animatable@^1.3.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/react-native-animatable/-/react-native-animatable-1.3.2.tgz#4783ee1a73dc98815aef234ce6b819f80bfe7d80"
+ integrity sha512-rmah3KQ63ft8DxkzFUwJSuZeq+oSYwldoGF4DTOR5WM2WR5wiWLgBAtrAHlI3Di3by323uOR21s+MlqPcHz2Kw==
+ dependencies:
+ prop-types "^15.5.10"
+
+react-native-branch@~3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/react-native-branch/-/react-native-branch-3.0.1.tgz#5b07b61cbd290168cd3c3662e017ebe0f356d2ca"
+ integrity sha512-vbcYxPZlpF5f39GAEUF8kuGQqCNeD3E6zEdvtOq8oCGZunHXlWlKgAS6dgBKCvsHvXgHuMtpvs39VgOp8DaKig==
+
+react-native-dotenv@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/react-native-dotenv/-/react-native-dotenv-0.2.0.tgz#311551cb6a35a3dcfede648bded55c0e3ece579d"
+ integrity sha1-MRVRy2o1o9z+3mSL3tVcDj7OV50=
+ dependencies:
+ babel-plugin-dotenv "0.1.1"
+
+react-native-elements@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/react-native-elements/-/react-native-elements-1.2.0.tgz#37f2562ca227d79dcb97ce0d869600f371041318"
+ integrity sha512-D6El6WXtKRGavHPgqJAk1UPMeDqxxfPDDjcuZ7S62uSg6CmyfXrPJLfQuSmJDC85Pm++oBq0Z5GRyymvvQObig==
+ dependencies:
+ "@types/react-native-vector-icons" "^6.4.1"
+ color "^3.1.0"
+ deepmerge "^3.1.0"
+ hoist-non-react-statics "^3.1.0"
+ opencollective-postinstall "^2.0.0"
+ prop-types "^15.7.2"
+ react-native-ratings "^6.3.0"
+ react-native-status-bar-height "^2.2.0"
+
+react-native-gesture-handler@^1.3.0:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-1.4.1.tgz#c38d9e57637b95e221722a79f2bafac62e3aeb21"
+ integrity sha512-Ffcs+SbEbkGaal0CClYL+v7A9y4OA5G5lW01qrzjxaqASp5C8BfnWSKuqYKE00s6bLwE5L4xnlHkG0yIxAtbrQ==
+ dependencies:
+ hammerjs "^2.0.8"
+ hoist-non-react-statics "^2.3.1"
+ invariant "^2.2.4"
+ prop-types "^15.7.2"
+
+react-native-modal-datetime-picker@^7.5.0:
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/react-native-modal-datetime-picker/-/react-native-modal-datetime-picker-7.5.0.tgz#415f02e29eedf283035867ae65d2e609af738abf"
+ integrity sha512-TtFS27IGhtg89LnIYIa8FrpNWbQENLiAAdfJPDdskv5VqIKgZSA0toOFYaAMRLeMiB8Urpf2/sAf0rdooCzu+g==
+ dependencies:
+ prop-types "^15.7.2"
+ react-native-modal "^11.0.2"
+
+react-native-modal@^11.0.2:
+ version "11.3.1"
+ resolved "https://registry.yarnpkg.com/react-native-modal/-/react-native-modal-11.3.1.tgz#9f67728b36fdc299f97d3da1d131190a0337b89b"
+ integrity sha512-3rRuXwvObknVijVNS8iamjMXWLjlb9xK90o+WtEcJ3C7HKuR2SOH578SoltIC6ZmVjO3vZwOApGVdSfR3LtPQg==
+ dependencies:
+ prop-types "^15.6.2"
+ react-native-animatable "^1.3.1"
+
+react-native-picker-select@^6.3.3:
+ version "6.3.3"
+ resolved "https://registry.yarnpkg.com/react-native-picker-select/-/react-native-picker-select-6.3.3.tgz#ea4d20a385041a2f4ead3eb3398b08511d7d6c2a"
+ integrity sha512-9cSXWonugev+e0EHrV8FhzwkjAhpipLFXsGMv+Ns5xI47T9fyNrOXpSeSfgnmycbuAbWRVlJRhJZ9eDGUaNk7w==
+ dependencies:
+ lodash.isequal "^4.5.0"
+
+react-native-ratings@^6.3.0:
+ version "6.5.0"
+ resolved "https://registry.yarnpkg.com/react-native-ratings/-/react-native-ratings-6.5.0.tgz#a1606ccba3c5b54eec8e6cfa4765a45cf0e4ab8d"
+ integrity sha512-YMcfQ7UQCmXGEc/WPlukHSHs5yvckTwjq5fTRk1FG8gaO7fZCNygEUGPuw4Dbvvp3IlsCUn0bOQd63RYsb7NDQ==
+ dependencies:
+ lodash "^4.17.4"
+ prop-types "^15.5.10"
+
+react-native-responsive-screen@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/react-native-responsive-screen/-/react-native-responsive-screen-1.2.2.tgz#59d82146b61c62510032fb795652335433f05f93"
+ integrity sha512-0UDQIBD+bW7+3NNt+ukSleUkKxbRiNupmzLQjdjyRoAjqxidN8+1gs0mHXfmhowcg2b875zTYrtQUG3022yHKw==
+
+react-native-safe-area-view@^0.14.1:
+ version "0.14.8"
+ resolved "https://registry.yarnpkg.com/react-native-safe-area-view/-/react-native-safe-area-view-0.14.8.tgz#ef33c46ff8164ae77acad48c3039ec9c34873e5b"
+ integrity sha512-MtRSIcZNstxv87Jet+UsPhEd1tpGe8cVskDXlP657x6rHpSrbrc+y13ZNXrwAgGNNhqQNX7UJT68ZIq//ZRmvw==
+ dependencies:
+ hoist-non-react-statics "^2.3.1"
+
+"react-native-screens@^1.0.0 || ^1.0.0-alpha":
+ version "1.0.0-alpha.23"
+ resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-1.0.0-alpha.23.tgz#25d7ea4d11bda4fcde2d1da7ae50271c6aa636e0"
+ integrity sha512-tOxHGQUN83MTmQB4ghoQkibqOdGiX4JQEmeyEv96MKWO/x8T2PJv84ECUos9hD3blPRQwVwSpAid1PPPhrVEaw==
+ dependencies:
+ debounce "^1.2.0"
+
+react-native-splash-screen@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/react-native-splash-screen/-/react-native-splash-screen-3.2.0.tgz#d47ec8557b1ba988ee3ea98d01463081b60fff45"
+ integrity sha512-Ls9qiNZzW/OLFoI25wfjjAcrf2DZ975hn2vr6U9gyuxi2nooVbzQeFoQS5vQcbCt9QX5NY8ASEEAtlLdIa6KVg==
+
+react-native-status-bar-height@^2.2.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/react-native-status-bar-height/-/react-native-status-bar-height-2.4.0.tgz#de8cee4bb733a196167210d2d0bc1fa10acba3e3"
+ integrity sha512-pWvZFlyIHiuxLugLioq97vXiaGSovFXEyxt76wQtbq0gxv4dGXMPqYow46UmpwOgeJpBhqL1E0EKxnfJRrFz5w==
+
+react-native-tab-view@^1.2.0, react-native-tab-view@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-1.4.1.tgz#f113cd87485808f0c991abec937f70fa380478b9"
+ integrity sha512-Bke8KkDcDhvB/z0AS7MnQKMD2p6Kwfc1rSKlMOvg9CC5CnClQ2QEnhPSbwegKDYhUkBI92iH/BYy7hNSm5kbUQ==
+ dependencies:
+ prop-types "^15.6.1"
+
+react-native-vector-icons@^6.6.0:
+ version "6.6.0"
+ resolved "https://registry.yarnpkg.com/react-native-vector-icons/-/react-native-vector-icons-6.6.0.tgz#66cf004918eb05d90778d64bd42077c1800d481b"
+ integrity sha512-MImKVx8JEvVVBnaShMr7/yTX4Y062JZMupht1T+IEgbqBj4aQeQ1z2SH4VHWKNtWtppk4kz9gYyUiMWqx6tNSw==
+ dependencies:
+ lodash "^4.0.0"
+ prop-types "^15.6.2"
+ yargs "^13.2.2"
+
+react-native-view-shot@2.6.0:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/react-native-view-shot/-/react-native-view-shot-2.6.0.tgz#3b23675826f67658366352c4b97b59a6aded2f43"
+ integrity sha512-yO9vWi/11m2hEJl8FrW1SMeVzFfPtMKh20MUInGqlsL0H8Ya2JGGlFfrBzx1KiFR2hFb5OdsTLYNtcVZtJ6pLQ==
+
+react-native-web@^0.11.4:
+ version "0.11.7"
+ resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.11.7.tgz#d173d5a9b58db23b6d442c4bc4c81e9939adac23"
+ integrity sha512-w1KAxX2FYLS2GAi3w3BnEZg/IUu7FdgHnLmFKHplRnHMV3u1OPB2EVA7ndNdfu7ds4Rn2OZjSXoNh6F61g3gkA==
+ dependencies:
+ array-find-index "^1.0.2"
+ create-react-class "^15.6.2"
+ debounce "^1.2.0"
+ deep-assign "^3.0.0"
+ fbjs "^1.0.0"
+ hyphenate-style-name "^1.0.2"
+ inline-style-prefixer "^5.0.3"
+ normalize-css-color "^1.0.2"
+ prop-types "^15.6.0"
+ react-timer-mixin "^0.13.4"
+
+"react-native@https://github.com/expo/react-native/archive/sdk-34.0.0.tar.gz":
+ version "0.59.8"
+ resolved "https://github.com/expo/react-native/archive/sdk-34.0.0.tar.gz#717c25bde6007a70e9f206ef4360999dae18e7b0"
+ dependencies:
+ "@babel/runtime" "^7.0.0"
+ "@react-native-community/cli" "^1.2.1"
+ absolute-path "^0.0.0"
+ art "^0.10.0"
+ base64-js "^1.1.2"
+ chalk "^2.4.1"
+ commander "^2.9.0"
+ compression "^1.7.1"
+ connect "^3.6.5"
+ create-react-class "^15.6.3"
+ debug "^2.2.0"
+ denodeify "^1.2.1"
+ errorhandler "^1.5.0"
+ escape-string-regexp "^1.0.5"
+ event-target-shim "^1.0.5"
+ fbjs "^1.0.0"
+ fbjs-scripts "^1.0.0"
+ fs-extra "^1.0.0"
+ glob "^7.1.1"
+ graceful-fs "^4.1.3"
+ inquirer "^3.0.6"
+ invariant "^2.2.4"
+ lodash "^4.17.5"
+ metro-babel-register "0.51.0"
+ metro-react-native-babel-transformer "0.51.0"
+ mime "^1.3.4"
+ minimist "^1.2.0"
+ mkdirp "^0.5.1"
+ morgan "^1.9.0"
+ node-fetch "^2.2.0"
+ node-notifier "^5.2.1"
+ npmlog "^2.0.4"
+ nullthrows "^1.1.0"
+ opn "^3.0.2"
+ optimist "^0.6.1"
+ plist "^3.0.0"
+ pretty-format "24.0.0-alpha.6"
+ promise "^7.1.1"
+ prop-types "^15.5.8"
+ react-clone-referenced-element "^1.0.1"
+ react-devtools-core "^3.6.0"
+ regenerator-runtime "^0.11.0"
+ rimraf "^2.5.4"
+ semver "^5.0.3"
+ serve-static "^1.13.1"
+ shell-quote "1.6.1"
+ stacktrace-parser "0.1.4"
+ ws "^1.1.5"
+ xmldoc "^0.4.0"
+ yargs "^9.0.0"
+
+react-navigation-drawer@~1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/react-navigation-drawer/-/react-navigation-drawer-1.4.0.tgz#70f3dd83e3da9cd4ea6e2739526502c823d466b9"
+ integrity sha512-ZyWBozcjB2aZ7vwCALv90cYA2NpDjM+WALaiYRshvPvue8l7cqynePbHK8GhlMGyJDwZqp4MxQmu8u1XAKp3Bw==
+ dependencies:
+ react-native-tab-view "^1.2.0"
+
+react-navigation-redux-helpers@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/react-navigation-redux-helpers/-/react-navigation-redux-helpers-3.0.2.tgz#612e1d5b1378ba0c17b6a2d8ec9a01cc698a3a16"
+ integrity sha512-+z7/eBGBpws/W3ffu7ayEl1YFMAbXO3Sgul3KIDyESI1BbmfSvKD2aRMEfE7AlO+58fJJsqWUMhNw+VACAdHjw==
+ dependencies:
+ invariant "^2.2.2"
+
+react-navigation-stack@~1.5.0:
+ version "1.5.4"
+ resolved "https://registry.yarnpkg.com/react-navigation-stack/-/react-navigation-stack-1.5.4.tgz#ec3f8468873b86aa2b344430dfc1d7e9801e39bd"
+ integrity sha512-jgbQ3qTRrfo6m5ibldRUzr7S5gf4qvrgqgVpQIyPLS8RXXO3n7eZmldqebmQPg6osaOVMjZ+kbV42n7LbdU4Mw==
+ dependencies:
+ prop-types "^15.7.2"
+
+react-navigation-tabs@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/react-navigation-tabs/-/react-navigation-tabs-1.2.0.tgz#602c147029bb4f1c569b26479ddba534fe3ebb19"
+ integrity sha512-I6vq3XX4ub9KhWQzcrggznls+2Z2C6w2ro46vokDGGvJ02CBpQRar7J0ETV29Ot5AJY67HucNUmZdH3yDFckmQ==
+ dependencies:
+ hoist-non-react-statics "^2.5.0"
+ prop-types "^15.6.1"
+ react-native-tab-view "^1.4.1"
+
+react-navigation@^3.12.0:
+ version "3.12.1"
+ resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-3.12.1.tgz#f86246e65030ab16160e9cbfa8585f3822450e38"
+ integrity sha512-WLjQis/A40cIMpFlw4o26nSLN+CvrZp8MGvJoL5K5H7DMZ/TdwgPa/Nm2sAQA27Hw7hOohQGj+diyxFRZi89Iw==
+ dependencies:
+ "@react-navigation/core" "~3.5.0"
+ "@react-navigation/native" "~3.6.1"
+ react-navigation-drawer "~1.4.0"
+ react-navigation-stack "~1.5.0"
+ react-navigation-tabs "~1.2.0"
+
+react-proxy@^1.1.7:
+ version "1.1.8"
+ resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a"
+ integrity sha1-nb/Z2SdSjDqp9ETkVYw3gwq4wmo=
+ dependencies:
+ lodash "^4.6.1"
+ react-deep-force-update "^1.0.0"
+
+react-redux@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.1.tgz#ce6eee1b734a7a76e0788b3309bf78ff6b34fa0a"
+ integrity sha512-QsW0vcmVVdNQzEkrgzh2W3Ksvr8cqpAv5FhEk7tNEft+5pp7rXxAudTz3VOPawRkLIepItpkEIyLcN/VVXzjTg==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ hoist-non-react-statics "^3.3.0"
+ invariant "^2.2.4"
+ loose-envify "^1.4.0"
+ prop-types "^15.7.2"
+ react-is "^16.9.0"
+
+react-timer-mixin@^0.13.4:
+ version "0.13.4"
+ resolved "https://registry.yarnpkg.com/react-timer-mixin/-/react-timer-mixin-0.13.4.tgz#75a00c3c94c13abe29b43d63b4c65a88fc8264d3"
+ integrity sha512-4+ow23tp/Tv7hBM5Az5/Be/eKKF7DIvJ09voz5LyHGQaqqz9WV8YMs31eFvcYQs7d451LSg7kDJV70XYN/Ug/Q==
+
+react-transform-hmr@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/react-transform-hmr/-/react-transform-hmr-1.0.4.tgz#e1a40bd0aaefc72e8dfd7a7cda09af85066397bb"
+ integrity sha1-4aQL0Krvxy6N/Xp82gmvhQZjl7s=
+ dependencies:
+ global "^4.3.0"
+ react-proxy "^1.1.7"
+
+react@16.8.3:
+ version "16.8.3"
+ resolved "https://registry.yarnpkg.com/react/-/react-16.8.3.tgz#c6f988a2ce895375de216edcfaedd6b9a76451d9"
+ integrity sha512-3UoSIsEq8yTJuSu0luO1QQWYbgGEILm+eJl2QN/VLDi7hL+EN18M3q3oVZwmVzzBJ3DkM7RMdRwBmZZ+b4IzSA==
+ dependencies:
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+ prop-types "^15.6.2"
+ scheduler "^0.13.3"
+
+read-pkg-up@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
+ integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=
+ dependencies:
+ find-up "^2.0.0"
+ read-pkg "^2.0.0"
+
+read-pkg@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
+ integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=
+ dependencies:
+ load-json-file "^2.0.0"
+ normalize-package-data "^2.3.2"
+ path-type "^2.0.0"
+
+readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6:
+ version "2.3.6"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
+ integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
+ dependencies:
+ 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"
+
+reduce-reducers@^0.1.0:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/reduce-reducers/-/reduce-reducers-0.1.5.tgz#ff77ca8068ff41007319b8b4b91533c7e0e54576"
+ integrity sha512-uoVmQnZQ+BtKKDKpBdbBri5SLNyIK9ULZGOA504++VbHcwouWE+fJDIo8AuESPF9/EYSkI0v05LDEQK6stCbTA==
+
+redux-actions@^0.10.1:
+ version "0.10.1"
+ resolved "https://registry.yarnpkg.com/redux-actions/-/redux-actions-0.10.1.tgz#bb442ee37dd9643a94933e4071e089f435587135"
+ integrity sha1-u0Qu433ZZDqUkz5AceCJ9DVYcTU=
+ dependencies:
+ reduce-reducers "^0.1.0"
+
+redux-form@^8.2.6:
+ version "8.2.6"
+ resolved "https://registry.yarnpkg.com/redux-form/-/redux-form-8.2.6.tgz#6840bbe9ed5b2aaef9dd82e6db3e5efcfddd69b1"
+ integrity sha512-krmF7wl1C753BYpEpWIVJ5NM4lUJZFZc5GFUVgblT+jprB99VVBDyBcgrZM3gWWLOcncFyNsHcKNQQcFg8Uanw==
+ dependencies:
+ "@babel/runtime" "^7.2.0"
+ es6-error "^4.1.1"
+ hoist-non-react-statics "^3.2.1"
+ invariant "^2.2.4"
+ is-promise "^2.1.0"
+ lodash "^4.17.15"
+ lodash-es "^4.17.15"
+ prop-types "^15.6.1"
+ react-is "^16.7.0"
+ react-lifecycles-compat "^3.0.4"
+ optionalDependencies:
+ immutable "3.8.2"
+
+redux-persist@^5.10.0:
+ version "5.10.0"
+ resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-5.10.0.tgz#5d8d802c5571e55924efc1c3a9b23575283be62b"
+ integrity sha512-sSJAzNq7zka3qVHKce1hbvqf0Vf5DuTVm7dr4GtsqQVOexnrvbV47RWFiPxQ8fscnyiuWyD2O92DOxPl0tGCRg==
+
+redux-saga@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-1.0.5.tgz#03317261bc5fa7ee2ecb778f4e4848f573557bbb"
+ integrity sha512-ytGFtaHyz6NQMQp7/LpQ/6CtOkbPRCcAeljUpc4c95hRm5U6dFvrRiZHeBPuQZ56L3oXfTX3hiYh8/3ZudsiNg==
+ dependencies:
+ "@redux-saga/core" "^1.0.3"
+
+redux-storage-merger-simple@^1.0.2:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/redux-storage-merger-simple/-/redux-storage-merger-simple-1.0.5.tgz#29a2886b0e770d9b70811aca800aa8efae89fb73"
+ integrity sha1-KaKIaw53DZtwgRrKgAqo766J+3M=
+ dependencies:
+ lodash.isobject "^3.0.2"
+ lodash.merge "^4.3.1"
+
+redux-storage@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/redux-storage/-/redux-storage-4.1.2.tgz#e06f4bdeee262aead9132fc9f7eadc67e9f9bea2"
+ integrity sha1-4G9L3u4mKurZEy/J9+rcZ+n5vqI=
+ dependencies:
+ lodash.isfunction "^3.0.8"
+ lodash.isobject "^3.0.2"
+ loose-envify "^1.2.0"
+ redux-actions "^0.10.1"
+ redux-storage-merger-simple "^1.0.2"
+
+"redux@>=0.10 <5":
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.4.tgz#4ee1aeb164b63d6a1bcc57ae4aa0b6e6fa7a3796"
+ integrity sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==
+ dependencies:
+ loose-envify "^1.4.0"
+ symbol-observable "^1.2.0"
+
+regenerate-unicode-properties@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
+ integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==
+ dependencies:
+ regenerate "^1.4.0"
+
+regenerate@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
+ integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
+
+regenerator-runtime@^0.11.0:
+ version "0.11.1"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+ integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+
+regenerator-runtime@^0.13.2:
+ version "0.13.3"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
+ integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==
+
+regenerator-transform@^0.14.0:
+ version "0.14.1"
+ resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb"
+ integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==
+ dependencies:
+ private "^0.1.6"
+
+regex-cache@^0.4.2:
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
+ integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==
+ dependencies:
+ is-equal-shallow "^0.1.3"
+
+regex-not@^1.0.0, regex-not@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
+ integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
+ dependencies:
+ extend-shallow "^3.0.2"
+ safe-regex "^1.1.0"
+
+regexp-tree@^0.1.6:
+ version "0.1.12"
+ resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.12.tgz#28eaaa6e66eeb3527c15108a3ff740d9e574e420"
+ integrity sha512-TsXZ8+cv2uxMEkLfgwO0E068gsNMLfuYwMMhiUxf0Kw2Vcgzq93vgl6wIlIYuPmfMqMjfQ9zAporiozqCnwLuQ==
+
+regexpp@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
+ integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==
+
+regexpu-core@^4.5.4:
+ version "4.5.5"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.5.tgz#aaffe61c2af58269b3e516b61a73790376326411"
+ integrity sha512-FpI67+ky9J+cDizQUJlIlNZFKual/lUkFr1AG6zOCpwZ9cLrg8UUVakyUQJD7fCDIe9Z2nwTQJNPyonatNmDFQ==
+ dependencies:
+ regenerate "^1.4.0"
+ regenerate-unicode-properties "^8.1.0"
+ regjsgen "^0.5.0"
+ regjsparser "^0.6.0"
+ unicode-match-property-ecmascript "^1.0.4"
+ unicode-match-property-value-ecmascript "^1.1.0"
+
+regjsgen@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd"
+ integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==
+
+regjsparser@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c"
+ integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==
+ dependencies:
+ jsesc "~0.5.0"
+
+remove-trailing-separator@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
+ integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
+
+repeat-element@^1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
+ integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
+
+repeat-string@^1.5.2, repeat-string@^1.6.1:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+ integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
+
+require-main-filename@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
+ integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
+
+require-main-filename@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+ integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+
+requires-port@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+ integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
+
+reselect@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147"
+ integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc=
+
+resolve-from@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+ integrity sha1-six699nWiBvItuZTM17rywoYh0g=
+
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve-url@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
+ integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
+
+resolve@^1.10.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.8.1:
+ version "1.12.0"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6"
+ integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==
+ dependencies:
+ path-parse "^1.0.6"
+
+restore-cursor@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
+ integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
+ dependencies:
+ onetime "^2.0.0"
+ signal-exit "^3.0.2"
+
+ret@~0.1.10:
+ version "0.1.15"
+ resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
+ integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+
+rimraf@2.6.3:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+ integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+ dependencies:
+ glob "^7.1.3"
+
+rimraf@^2.5.4, rimraf@^2.6.1:
+ version "2.7.1"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+ integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+ dependencies:
+ glob "^7.1.3"
+
+rimraf@~2.2.6:
+ version "2.2.8"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582"
+ integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=
+
+rn-fetch-blob@^0.10.16:
+ version "0.10.16"
+ resolved "https://registry.yarnpkg.com/rn-fetch-blob/-/rn-fetch-blob-0.10.16.tgz#bd54f66c94f7a8e06c213077483646478ae8d230"
+ integrity sha512-hZV+nF0HK4CWmspXGMw7/G8Q8qugpS/wbKiNLsFpdBZR8XYzjFZNvBWgGyC0F5JWQn3sjmK2w/FJjBlwdQWNQg==
+ dependencies:
+ base-64 "0.1.0"
+ glob "7.0.6"
+
+rsvp@^3.3.3:
+ version "3.6.2"
+ resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a"
+ integrity sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==
+
+rtl-detect@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.0.2.tgz#8eca316f5c6563d54df4e406171dd7819adda67f"
+ integrity sha512-5X1422hvphzg2a/bo4tIDbjFjbJUOaPZwqE6dnyyxqwFqfR+tBcvfqapJr0o0VygATVCGKiODEewhZtKF+90AA==
+
+run-async@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
+ integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA=
+ dependencies:
+ is-promise "^2.1.0"
+
+rx-lite-aggregates@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
+ integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=
+ dependencies:
+ rx-lite "*"
+
+rx-lite@*, rx-lite@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
+ integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=
+
+rxjs@^6.4.0:
+ version "6.5.3"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a"
+ integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==
+ dependencies:
+ tslib "^1.9.0"
+
+safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-buffer@^5.1.2:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
+ integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
+
+safe-regex@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
+ integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
+ dependencies:
+ ret "~0.1.10"
+
+"safer-buffer@>= 2.1.2 < 3":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+sane@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/sane/-/sane-3.1.0.tgz#995193b7dc1445ef1fe41ddfca2faf9f111854c6"
+ integrity sha512-G5GClRRxT1cELXfdAq7UKtUsv8q/ZC5k8lQGmjEm4HcAl3HzBy68iglyNCmw4+0tiXPCBZntslHlRhbnsSws+Q==
+ dependencies:
+ anymatch "^2.0.0"
+ capture-exit "^1.2.0"
+ exec-sh "^0.2.0"
+ execa "^1.0.0"
+ fb-watchman "^2.0.0"
+ micromatch "^3.1.4"
+ minimist "^1.1.1"
+ walker "~1.0.5"
+ watch "~0.18.0"
+ optionalDependencies:
+ fsevents "^1.2.3"
+
+sax@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+ integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
+sax@~1.1.1:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.6.tgz#5d616be8a5e607d54e114afae55b7eaf2fcc3240"
+ integrity sha1-XWFr6KXmB9VOEUr65Vt+ry/MMkA=
+
+scheduler@^0.13.3:
+ version "0.13.6"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889"
+ integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==
+ dependencies:
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+
+scheduler@^0.15.0:
+ version "0.15.0"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.15.0.tgz#6bfcf80ff850b280fed4aeecc6513bc0b4f17f8e"
+ integrity sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==
+ dependencies:
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+
+"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+ integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+semver@^6.1.2, semver@^6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+ integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+send@0.17.1:
+ version "0.17.1"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
+ integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
+ dependencies:
+ 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"
+
+serialize-error@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a"
+ integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go=
+
+serve-static@^1.13.1:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
+ integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
+ dependencies:
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ parseurl "~1.3.3"
+ send "0.17.1"
+
+set-blocking@^2.0.0, set-blocking@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+ integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
+set-value@^2.0.0, set-value@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
+ integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==
+ dependencies:
+ extend-shallow "^2.0.1"
+ is-extendable "^0.1.1"
+ is-plain-object "^2.0.3"
+ split-string "^3.0.1"
+
+setimmediate@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+ integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
+
+setprototypeof@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
+ integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
+
+shebang-command@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+ integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
+ dependencies:
+ shebang-regex "^1.0.0"
+
+shebang-regex@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+ integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
+
+shell-quote@1.6.1:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
+ integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=
+ dependencies:
+ array-filter "~0.0.0"
+ array-map "~0.0.0"
+ array-reduce "~0.0.0"
+ jsonify "~0.0.0"
+
+shell-quote@^1.6.1:
+ version "1.7.1"
+ resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.1.tgz#3161d969886fb14f9140c65245a5dd19b6f0b06b"
+ integrity sha512-2kUqeAGnMAu6YrTPX4E3LfxacH9gKljzVjlkUeSqY0soGwK4KLl7TURXCem712tkhBCeeaFP9QK4dKn88s3Icg==
+
+shellwords@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
+ integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
+
+signal-exit@^3.0.0, signal-exit@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+ integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+
+simple-plist@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.0.0.tgz#bed3085633b22f371e111f45d159a1ccf94b81eb"
+ integrity sha512-043L2rO80LVF7zfZ+fqhsEkoJFvW8o59rt/l4ctx1TJWoTx7/jkiS1R5TatD15Z1oYnuLJytzE7gcnnBuIPL2g==
+ dependencies:
+ bplist-creator "0.0.7"
+ bplist-parser "0.1.1"
+ plist "^3.0.1"
+
+simple-swizzle@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
+ integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=
+ dependencies:
+ is-arrayish "^0.3.1"
+
+slash@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
+ integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
+
+slice-ansi@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
+ integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==
+ dependencies:
+ ansi-styles "^3.2.0"
+ astral-regex "^1.0.0"
+ is-fullwidth-code-point "^2.0.0"
+
+slide@^1.1.5:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
+ integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=
+
+snapdragon-node@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
+ integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
+ dependencies:
+ define-property "^1.0.0"
+ isobject "^3.0.0"
+ snapdragon-util "^3.0.1"
+
+snapdragon-util@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
+ integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
+ dependencies:
+ kind-of "^3.2.0"
+
+snapdragon@^0.8.1:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
+ integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
+ dependencies:
+ base "^0.11.1"
+ debug "^2.2.0"
+ define-property "^0.2.5"
+ extend-shallow "^2.0.1"
+ map-cache "^0.2.2"
+ source-map "^0.5.6"
+ source-map-resolve "^0.5.0"
+ use "^3.1.0"
+
+source-map-resolve@^0.5.0:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
+ integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==
+ dependencies:
+ atob "^2.1.1"
+ decode-uri-component "^0.2.0"
+ resolve-url "^0.2.1"
+ source-map-url "^0.4.0"
+ urix "^0.1.0"
+
+source-map-support@^0.5.9:
+ version "0.5.13"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
+ integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map-url@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
+ integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
+
+source-map@^0.5.0, source-map@^0.5.6:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+ integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+
+source-map@^0.6.0, source-map@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+spdx-correct@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
+ integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
+ dependencies:
+ spdx-expression-parse "^3.0.0"
+ spdx-license-ids "^3.0.0"
+
+spdx-exceptions@^2.1.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
+ integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
+
+spdx-expression-parse@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
+ integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
+ dependencies:
+ spdx-exceptions "^2.1.0"
+ spdx-license-ids "^3.0.0"
+
+spdx-license-ids@^3.0.0:
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
+ integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
+
+split-on-first@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
+ integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
+
+split-string@^3.0.1, split-string@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
+ integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
+ dependencies:
+ extend-shallow "^3.0.0"
+
+sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+ integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
+
+stacktrace-parser@0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.4.tgz#01397922e5f62ecf30845522c95c4fe1d25e7d4e"
+ integrity sha1-ATl5IuX2Ls8whFUiyVxP4dJefU4=
+
+static-extend@^0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
+ integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
+ dependencies:
+ define-property "^0.2.5"
+ object-copy "^0.1.0"
+
+"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+ integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
+
+stream-buffers@~2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4"
+ integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ=
+
+strict-uri-encode@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
+ integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
+
+string-width@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+ integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ strip-ansi "^3.0.0"
+
+"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
+ integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
+ dependencies:
+ is-fullwidth-code-point "^2.0.0"
+ strip-ansi "^4.0.0"
+
+string-width@^3.0.0, string-width@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
+ integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
+ dependencies:
+ emoji-regex "^7.0.1"
+ is-fullwidth-code-point "^2.0.0"
+ strip-ansi "^5.1.0"
+
+string_decoder@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+ integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+ dependencies:
+ safe-buffer "~5.1.0"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+ integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
+ dependencies:
+ ansi-regex "^2.0.0"
+
+strip-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+ integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
+ dependencies:
+ ansi-regex "^3.0.0"
+
+strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
+ integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
+ dependencies:
+ ansi-regex "^4.1.0"
+
+strip-bom@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+ integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
+
+strip-eof@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
+ integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
+
+strip-json-comments@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7"
+ integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==
+
+strip-json-comments@~2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+ integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
+
+supports-color@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+ integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
+
+supports-color@^5.3.0:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+ dependencies:
+ has-flag "^3.0.0"
+
+supports-color@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
+ integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
+ dependencies:
+ has-flag "^3.0.0"
+
+symbol-observable@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
+ integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
+
+table@^5.2.3:
+ version "5.4.6"
+ resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
+ integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==
+ dependencies:
+ ajv "^6.10.2"
+ lodash "^4.17.14"
+ slice-ansi "^2.1.0"
+ string-width "^3.0.0"
+
+tar@^4:
+ version "4.4.10"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1"
+ integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==
+ dependencies:
+ chownr "^1.1.1"
+ fs-minipass "^1.2.5"
+ minipass "^2.3.5"
+ minizlib "^1.2.1"
+ mkdirp "^0.5.0"
+ safe-buffer "^5.1.2"
+ yallist "^3.0.3"
+
+temp@0.8.3:
+ version "0.8.3"
+ resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59"
+ integrity sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=
+ dependencies:
+ os-tmpdir "^1.0.0"
+ rimraf "~2.2.6"
+
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
+
+throat@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
+ integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=
+
+through2@^2.0.0:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
+ integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
+ dependencies:
+ readable-stream "~2.3.6"
+ xtend "~4.0.1"
+
+through@^2.3.6:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+ integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
+
+time-stamp@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3"
+ integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=
+
+tiny-queue@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046"
+ integrity sha1-JaZ/LG4lOyypQZd7XvdELvl6YEY=
+
+tmp@^0.0.33:
+ version "0.0.33"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+ integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
+ dependencies:
+ os-tmpdir "~1.0.2"
+
+tmpl@1.0.x:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
+ integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=
+
+to-fast-properties@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+ integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
+
+to-object-path@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
+ integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
+ dependencies:
+ kind-of "^3.0.2"
+
+to-regex-range@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
+ integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
+ dependencies:
+ is-number "^3.0.0"
+ repeat-string "^1.6.1"
+
+to-regex@^3.0.1, to-regex@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
+ integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
+ dependencies:
+ define-property "^2.0.2"
+ extend-shallow "^3.0.2"
+ regex-not "^1.0.2"
+ safe-regex "^1.1.0"
+
+toidentifier@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
+ integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
+
+trim-right@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
+ integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
+
+tslib@^1.9.0:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
+ integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
+
+type-check@~0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+ integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
+ dependencies:
+ prelude-ls "~1.1.2"
+
+typedarray@^0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+ integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
+
+typescript-compare@^0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/typescript-compare/-/typescript-compare-0.0.2.tgz#7ee40a400a406c2ea0a7e551efd3309021d5f425"
+ integrity sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==
+ dependencies:
+ typescript-logic "^0.0.0"
+
+typescript-logic@^0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/typescript-logic/-/typescript-logic-0.0.0.tgz#66ebd82a2548f2b444a43667bec120b496890196"
+ integrity sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==
+
+typescript-tuple@^2.1.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/typescript-tuple/-/typescript-tuple-2.2.1.tgz#7d9813fb4b355f69ac55032e0363e8bb0f04dad2"
+ integrity sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==
+ dependencies:
+ typescript-compare "^0.0.2"
+
+ua-parser-js@^0.7.18, ua-parser-js@^0.7.19:
+ version "0.7.20"
+ resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.20.tgz#7527178b82f6a62a0f243d1f94fd30e3e3c21098"
+ integrity sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw==
+
+uglify-es@^3.1.9:
+ version "3.3.9"
+ resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
+ integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==
+ dependencies:
+ commander "~2.13.0"
+ source-map "~0.6.1"
+
+ultron@1.0.x:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa"
+ integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=
+
+ultron@~1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
+ integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==
+
+unicode-canonical-property-names-ecmascript@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
+ integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==
+
+unicode-match-property-ecmascript@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c"
+ integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==
+ dependencies:
+ unicode-canonical-property-names-ecmascript "^1.0.4"
+ unicode-property-aliases-ecmascript "^1.0.4"
+
+unicode-match-property-value-ecmascript@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277"
+ integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==
+
+unicode-property-aliases-ecmascript@^1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57"
+ integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==
+
+unimodules-barcode-scanner-interface@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/unimodules-barcode-scanner-interface/-/unimodules-barcode-scanner-interface-3.0.0.tgz#2ec52201ee1f0e10af3b03ed49862d6b6937cf10"
+ integrity sha512-EtJBfKU5VgZbyIfIZwyWfUo59pIgW6s7YGzlpj9jk4UWKyqqhYT/FoaZqudCJcPcfh2eYxkc9VxBGieRBpQrzg==
+
+unimodules-camera-interface@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/unimodules-camera-interface/-/unimodules-camera-interface-3.0.0.tgz#2869f0868a9e2c65bd2346f0a67d93bc96509676"
+ integrity sha512-STjf1FAdYlN27ilJSR4kIUYyHTPrkQSR/mEg4S4pZX6tazmcuG2KzLCXCoV+xMWsrwmsMBjgLzw6yzg87N5Ydw==
+
+unimodules-constants-interface@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/unimodules-constants-interface/-/unimodules-constants-interface-3.0.0.tgz#991f823369da27362e8633a7dac680fb530e5569"
+ integrity sha512-S4ap11UJH7D+Y4fXC7DyMNAkqIWD8B7rNCTS30wAF9beHXMZa1Od66rkJgSHqFRURy06h+Jr7qfJm9H5mtMz8Q==
+
+unimodules-face-detector-interface@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/unimodules-face-detector-interface/-/unimodules-face-detector-interface-3.0.0.tgz#5752a00156a6de470944161040b845a1f1ae84b0"
+ integrity sha512-fMQ3ZnhdOjbQ5ZXW62s/t1bbqBaenxzVIcgVEcwvLIFek0mx/EMHFkySgFkFjU11icUvaPEXW1yJtkK4QEpLhg==
+
+unimodules-file-system-interface@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/unimodules-file-system-interface/-/unimodules-file-system-interface-3.0.0.tgz#0ada7a89e3046d2fa4dd1853b867fe8ae3994561"
+ integrity sha512-LkLIKRE3CwsXLRFw8vx0++Cfjj+pAvvidVb7yhGWKFmNlVaWUW9Z8jkhFLBFXDsGFAOU69bUTrz25jmB2MRt0Q==
+
+unimodules-font-interface@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/unimodules-font-interface/-/unimodules-font-interface-3.0.0.tgz#e38dfc0932e9a84c5b8091eeb6735170fa86d85e"
+ integrity sha512-DOQI0uTn7CGvA9lNUuiTWfQYuKQEM8LZKn6gNS8G+HVHVb+TZl/37qdhuoMBi5jkAZ4VOD/GpgnPv8qr0pJi1Q==
+
+unimodules-image-loader-interface@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/unimodules-image-loader-interface/-/unimodules-image-loader-interface-3.0.0.tgz#49e371fdf3fc4acf382f726cfac643d5c08b051f"
+ integrity sha512-hC/VWdT33GkOZ4FLaqPoKGNKxhw+miFhM+7Re57snWIWYewSv0lRvCqqwc/hbGLocvd2qF3YYrBx9woqPI8NzA==
+
+unimodules-permissions-interface@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/unimodules-permissions-interface/-/unimodules-permissions-interface-3.0.0.tgz#c8396a1b697b116801cfcb3b52466b87380a5b78"
+ integrity sha512-rfyGDBMtO8IOlk9hJN44EKz7vk6nt/PXByAumsptRdgsd+knokMlaWGYatrxKW2g/08WUbEkgKspvMxjJ0M1Tg==
+
+unimodules-sensors-interface@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/unimodules-sensors-interface/-/unimodules-sensors-interface-3.0.0.tgz#9591b7015fae5c2752652a4cdc294f7734489ea1"
+ integrity sha512-1JJT/lqCfxHqUSJc3o6b0WUply/lFOJjcuzN0QcAfmdAW8d+lEXA7BJ7DV/Nn/OKpMlHriEyxkM+FoGoXKJJcg==
+
+unimodules-task-manager-interface@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/unimodules-task-manager-interface/-/unimodules-task-manager-interface-3.0.0.tgz#26f31786eb54dfa5839ca71bf9a77b9c2b4cf4cb"
+ integrity sha512-og4UiUOxc7PqT8uQQqXY+pOBvdS204xmgyUG2AjM2L3kVsw/6WH4pIW084WG8/e9M5SLsSXdrjecIUBQ/zLf8w==
+
+union-value@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
+ integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==
+ dependencies:
+ arr-union "^3.1.0"
+ get-value "^2.0.6"
+ is-extendable "^0.1.1"
+ set-value "^2.0.1"
+
+universalify@^0.1.0:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+ integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
+unpipe@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+ integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
+
+unset-value@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
+ integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
+ dependencies:
+ has-value "^0.3.1"
+ isobject "^3.0.0"
+
+uri-js@^4.2.2:
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
+ integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+ dependencies:
+ punycode "^2.1.0"
+
+urix@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
+ integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
+
+url-parse@^1.4.4:
+ version "1.4.7"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
+ integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
+ dependencies:
+ querystringify "^2.1.1"
+ requires-port "^1.0.0"
+
+use@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
+ integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
+
+util-deprecate@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
+utils-merge@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+ integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
+
+uuid-js@^0.7.5:
+ version "0.7.5"
+ resolved "https://registry.yarnpkg.com/uuid-js/-/uuid-js-0.7.5.tgz#6c886d02a53d2d40dcf25d91a170b4a7b25b94d0"
+ integrity sha1-bIhtAqU9LUDc8l2RoXC0p7JblNA=
+
+uuid@^3.3.2:
+ version "3.3.3"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
+ integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==
+
+v8-compile-cache@^2.0.3:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
+ integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==
+
+validate-npm-package-license@^3.0.1:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
+ integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
+ dependencies:
+ spdx-correct "^3.0.0"
+ spdx-expression-parse "^3.0.0"
+
+vary@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+ integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
+
+walker@~1.0.5:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
+ integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=
+ dependencies:
+ makeerror "1.0.x"
+
+watch@~0.18.0:
+ version "0.18.0"
+ resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986"
+ integrity sha1-KAlUdsbffJDJYxOJkMClQj60uYY=
+ dependencies:
+ exec-sh "^0.2.0"
+ minimist "^1.2.0"
+
+whatwg-fetch@>=0.10.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
+ integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==
+
+which-module@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+ integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
+
+which@^1.2.9, which@^1.3.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+ integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+ dependencies:
+ isexe "^2.0.0"
+
+wide-align@^1.1.0:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
+ integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
+ dependencies:
+ string-width "^1.0.2 || 2"
+
+wordwrap@^1.0.0, wordwrap@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+ integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
+
+wordwrap@~0.0.2:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
+ integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc=
+
+wrap-ansi@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
+ integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+
+wrap-ansi@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
+ integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==
+ dependencies:
+ ansi-styles "^3.2.0"
+ string-width "^3.0.0"
+ strip-ansi "^5.0.0"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+write-file-atomic@^1.2.0:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f"
+ integrity sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=
+ dependencies:
+ graceful-fs "^4.1.11"
+ imurmurhash "^0.1.4"
+ slide "^1.1.5"
+
+write@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3"
+ integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==
+ dependencies:
+ mkdirp "^0.5.1"
+
+ws@^1.1.0, ws@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51"
+ integrity sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==
+ dependencies:
+ options ">=0.0.5"
+ ultron "1.0.x"
+
+ws@^3.3.1:
+ version "3.3.3"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
+ integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==
+ dependencies:
+ async-limiter "~1.0.0"
+ safe-buffer "~5.1.0"
+ ultron "~1.1.0"
+
+xcode@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/xcode/-/xcode-2.0.0.tgz#134f1f94c26fbfe8a9aaa9724bfb2772419da1a2"
+ integrity sha512-5xF6RCjAdDEiEsbbZaS/gBRt3jZ/177otZcpoLCjGN/u1LrfgH7/Sgeeavpr/jELpyDqN2im3AKosl2G2W8hfw==
+ dependencies:
+ simple-plist "^1.0.0"
+ uuid "^3.3.2"
+
+xmlbuilder@^9.0.7:
+ version "9.0.7"
+ resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
+ integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
+
+xmldoc@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-0.4.0.tgz#d257224be8393eaacbf837ef227fd8ec25b36888"
+ integrity sha1-0lciS+g5PqrL+DfvIn/Y7CWzaIg=
+ dependencies:
+ sax "~1.1.1"
+
+xmldom@0.1.x:
+ version "0.1.27"
+ resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9"
+ integrity sha1-1QH5ezvbQDr4757MIFcxh6rawOk=
+
+xpipe@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/xpipe/-/xpipe-1.0.5.tgz#8dd8bf45fc3f7f55f0e054b878f43a62614dafdf"
+ integrity sha1-jdi/Rfw/f1Xw4FS4ePQ6YmFNr98=
+
+xtend@~4.0.1:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
+ integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
+
+y18n@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
+ integrity sha1-bRX7qITAhnnA136I53WegR4H+kE=
+
+y18n@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
+ integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
+
+yallist@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
+ integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
+
+yallist@^3.0.0, yallist@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
+ integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
+
+yargs-parser@^13.1.1:
+ version "13.1.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0"
+ integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==
+ dependencies:
+ camelcase "^5.0.0"
+ decamelize "^1.2.0"
+
+yargs-parser@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9"
+ integrity sha1-jQrELxbqVd69MyyvTEA4s+P139k=
+ dependencies:
+ camelcase "^4.1.0"
+
+yargs@^13.2.2:
+ version "13.3.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83"
+ integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==
+ dependencies:
+ cliui "^5.0.0"
+ find-up "^3.0.0"
+ get-caller-file "^2.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^2.0.0"
+ set-blocking "^2.0.0"
+ string-width "^3.0.0"
+ which-module "^2.0.0"
+ y18n "^4.0.0"
+ yargs-parser "^13.1.1"
+
+yargs@^9.0.0:
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-9.0.1.tgz#52acc23feecac34042078ee78c0c007f5085db4c"
+ integrity sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=
+ dependencies:
+ camelcase "^4.1.0"
+ cliui "^3.2.0"
+ decamelize "^1.1.1"
+ get-caller-file "^1.0.1"
+ os-locale "^2.0.0"
+ read-pkg-up "^2.0.0"
+ require-directory "^2.1.1"
+ require-main-filename "^1.0.1"
+ set-blocking "^2.0.0"
+ string-width "^2.0.0"
+ which-module "^2.0.0"
+ y18n "^3.2.1"
+ yargs-parser "^7.0.0"