diff --git a/.eslintrc b/.eslintrc index ff1824d3d0fa52b0f1b692f052af6e7e353461ce..c76f459403a3c48fa7cc7760291f6378c38e12f8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -34,7 +34,7 @@ "error", { "types": { - // "object": false + "object": "false" } } ], @@ -120,7 +120,7 @@ "import/no-internal-modules": "off", "import/order": "off", "indent": [2, 2, {"SwitchCase": 1}], - // "no-restricted-imports": ["error", {"patterns": ["../*"]}], + "no-restricted-imports": ["error", {"patterns": ["../*"]}], "max-classes-per-file": [ "error", 10 diff --git a/config/config.ts b/config/config.ts deleted file mode 100644 index c320485f1c88e1fb20cb9c73586813f9766acffc..0000000000000000000000000000000000000000 --- a/config/config.ts +++ /dev/null @@ -1,20 +0,0 @@ -export class Config { - public apiUrl: string; - public clientId: string; - public clientKey: string; - public filter: Record; - - public constructor(config: Config) { - this.apiUrl = config.apiUrl; - this.clientId = config.clientId; - this.clientKey = config.clientKey; - this.filter = config.filter; - } -} - -export const DefaultConfig = new Config( { - apiUrl: 'https://api.sandbox.genesys-pgr.org', - clientId: 'clientid@genesys', - clientKey: 'changeme', - filter: {}, -}) diff --git a/entrypoints/client.tsx b/entrypoints/client.tsx index 008474d951935dd7e35f485d6744ffe4b21641c3..595b775c5811331f5698ed9e72233c5abe0a12ce 100644 --- a/entrypoints/client.tsx +++ b/entrypoints/client.tsx @@ -1,10 +1,21 @@ -import { showGenesysUI } from 'genesys'; +import { Config } from 'config/config'; +import { + showGenesysUI, + // showOverview, // for testing +} from 'genesys'; const queryLang = document.location.search && document.location.search.substr(1) || undefined; -showGenesysUI(document.getElementById('genesys'), { +const genesysConfig: Config = { + apiUrl: 'http://localhost:8080', clientId: 'defaultclient@localhost', clientKey: 'changeme', - apiUrl: 'http://localhost:8080', filter: { institute: { code: [ 'COL003', 'BEL084', 'ETH013' ] } }, -}, queryLang); + language: queryLang, + shoppingCart: { + enabled: true, + }, +}; + +showGenesysUI(document.getElementById('genesys'), genesysConfig); +// showOverview(document.getElementById('genesys'), genesysConfig); diff --git a/package.json b/package.json index 26c7325615fd78c228982fe5f79a1b5a22385b41..b57d69f5851e9ca262e10ca1efdf1f5b087847d0 100644 --- a/package.json +++ b/package.json @@ -36,11 +36,15 @@ "es-cookie": "^1.0.0", "history": "^4.0.0", "i18next": "^19.0.0", + "immutability-helper": "^3.0.0", "react": "^16.0.0", "react-dom": "^16.0.0", "react-google-recaptcha": "^2.0.0", "react-i18next": "^11.0.0", - "react-router-dom": "^5.0.0" + "react-redux": "^7.0.0", + "react-router-dom": "^5.0.0", + "redux": "^4.0.0", + "redux-thunk": "^2.0.0" }, "devDependencies": { "@types/react": "^16.0.0", @@ -63,6 +67,7 @@ "html-webpack-plugin": "4.0.0-alpha", "mini-css-extract-plugin": "^0.9.0", "optimize-css-assets-webpack-plugin": "^5.0.0", + "redux-devtools-extension": "^2.13.8", "rimraf": "^3.0.0", "script-ext-html-webpack-plugin": "^2.0.0", "tslint": "^6.0.0", diff --git a/src/accession/AccessionDetailsPage.tsx b/src/accession/AccessionDetailsPage.tsx index cdaa7a5631d92eb8f09f8a256f6b0644276906ee..6aa79fb9f9f1d89556b435798e0fb904c7179666 100644 --- a/src/accession/AccessionDetailsPage.tsx +++ b/src/accession/AccessionDetailsPage.tsx @@ -1,10 +1,12 @@ import React from 'react'; +import { connect } from 'react-redux'; import { AccessionService } from '@genesys/client/service'; import { Property } from 'ui/common/Property'; import AccessionDetails from '@genesys/client/model/accession/AccessionDetails'; import { WithTranslation, withTranslation } from 'react-i18next'; import { LocalStorageCart } from 'utilities'; +import { WithConfig } from 'config/config'; interface IAccessionDetailsPageState { accession: AccessionDetails; @@ -13,10 +15,9 @@ interface IAccessionDetailsPageState { interface IAccessionDetailsPage { match: any; - apiUrl: string; } -class AccessionDetailsPage extends React.Component { +class AccessionDetailsPage extends React.Component { public constructor(props) { super(props); } @@ -91,7 +92,7 @@ class AccessionDetailsPage extends React.Component } -
- {cartItems.includes(this.props.match.params.uuid) ? - - : - this.renderAddToCart() - } -
+ { shoppingCart.enabled && +
+ { cartItems.includes(this.props.match.params.uuid) ? + + : + this.renderAddToCart() + } +
+ } ); } }; } -export default withTranslation()(AccessionDetailsPage); +const mapStateToProps = (state) => ({ + appConfig: state.appConfig.config, +}); + +export default connect(mapStateToProps)(withTranslation()(AccessionDetailsPage)); diff --git a/src/accession/AccessionListPage.tsx b/src/accession/AccessionListPage.tsx index 40d1ecb4b87303c507c4c4928e46ba17012b1a33..00952781b763b0ea678765126e1565fcfa67a57f 100644 --- a/src/accession/AccessionListPage.tsx +++ b/src/accession/AccessionListPage.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { parse } from 'query-string'; +import { connect } from 'react-redux'; import { AccessionService } from '@genesys/client/service'; import AccessionFilter from '@genesys/client/model/accession/AccessionFilter'; @@ -10,6 +11,7 @@ import Pagination from 'ui/common/Pagination'; import { AccessionFilters } from './AccessionFilters'; import { withTranslation, WithTranslation } from 'react-i18next'; import { LocalStorageCart } from 'utilities'; +import { WithConfig } from 'config/config'; interface IAccessionListPageState { filter: AccessionFilter; @@ -20,16 +22,15 @@ interface IAccessionListPageState { } interface IAccessionListPageProps { - filter: AccessionFilter; location: any; } -class AccessionListPage extends React.Component { +class AccessionListPage extends React.Component { public constructor(props) { super(props); this.state = { - filter: this.props.filter, + filter: this.props.appConfig.filter, accessions: null, selected: [], isAllSelected: false, @@ -68,7 +69,7 @@ class AccessionListPage extends React.Component selectedUUIDs.add(uuid)); @@ -187,48 +188,54 @@ class AccessionListPage extends React.Component - - - + { shoppingCart.enabled && ( + + + + ) } { t('accession.crop') } { t('accession.acceNumb') } { t('accession.accessionName') } { t('accession.taxonomy') } { t('accession.countryOfOrigin') } { t('accession.sampStat') } - { t('list.availability') } + { shoppingCart.enabled && ( { t('list.availability') } ) } { accessions.content.map((a, i) => ( - - {this.canAddToCart(a) && - - } - + { shoppingCart.enabled && ( + + { this.canAddToCart(a) && + + } + + ) } { a.cropName } { a.accessionNumber } { a.accessionName } { a.countryOfOrigin && a.countryOfOrigin.name } { a.sampStat && t(`accession.sampleStatus.${a.sampStat}`) } - - {this.renderCartButton(a, i)} - + { shoppingCart.enabled && + + { this.renderCartButton(a, i) } + + } )) } @@ -240,4 +247,8 @@ class AccessionListPage extends React.Component ({ + appConfig: state.appConfig.config, +}); + +export default connect(mapStateToProps)(withTranslation()(AccessionListPage)); diff --git a/src/accession/AccessionOverviewSection.tsx b/src/accession/AccessionOverviewSection.tsx index 39caf5b4db1d40da02565470b83405d42ebad66d..a23a46e40c9032e0b03b93ab6f67bdd703fcee6e 100644 --- a/src/accession/AccessionOverviewSection.tsx +++ b/src/accession/AccessionOverviewSection.tsx @@ -3,39 +3,25 @@ import { withTranslation, WithTranslation } from 'react-i18next'; // model import AccessionOverview from '@genesys/client/model/accession/AccessionOverview'; import PropertiesCard from 'ui/common/PropertiesCard'; -import { VocabularyService } from '@genesys/client/service'; +import { connect } from 'react-redux'; -interface IAccessionOverviewPageState { - countryCodes: object; -} interface IAccessionOverviewPageProps extends React.ClassAttributes { overview: AccessionOverview; + countryCodes: Record; } -class AccessionOverviewSection extends React.Component { +class AccessionOverviewSection extends React.Component { public constructor(props) { super(props); - - this.state = { - countryCodes: null, - } } private overviewKeys = ['institute.code', 'institute.country.code3', 'cropName', 'crop.shortName', 'sampStat', 'taxonomy.genus', 'taxonomy.genusSpecies', 'taxonomy.grinTaxonomySpecies.name', 'taxonomy.currentTaxonomySpecies.name', 'countryOfOrigin.code3', 'donorCode', 'mlsStatus', 'available', 'duplSite', 'sgsv', 'storage', 'breederCode', 'aegis']; - public async componentDidMount() { - const codes = await VocabularyService.decode3166Alpha3Terms(this.props.i18n.language); - this.setState({ countryCodes: codes }) - } - public render() { - const { overview, t } = this.props; - - const { countryCodes } = this.state; - + const { overview, t, countryCodes } = this.props; if (!overview) { return null; @@ -206,4 +192,8 @@ class AccessionOverviewSection extends React.Component ({ + countryCodes: state.decoding.countryCodes, +}); + +export default connect(mapStateToProps)(withTranslation()(AccessionOverviewSection)); diff --git a/src/accession/OverviewPage.tsx b/src/accession/OverviewPage.tsx index a41d593b60754eccfada24baf8d572d8c4949d0e..531caaa2459535fb55f79791ca170fffed81f485 100644 --- a/src/accession/OverviewPage.tsx +++ b/src/accession/OverviewPage.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { WithTranslation, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; // Models import { AccessionService } from '@genesys/client/service'; @@ -63,4 +64,8 @@ class BrowsePage extends React.Component { } } -export default withTranslation()(BrowsePage); +const mapStateToProps = (state) => ({ + filter: state.appConfig.config.filter, +}); + +export default connect(mapStateToProps)(withTranslation()(BrowsePage)); diff --git a/src/config/config.ts b/src/config/config.ts new file mode 100644 index 0000000000000000000000000000000000000000..aae51696dcd37826cbb798150fedcdb8f59aad02 --- /dev/null +++ b/src/config/config.ts @@ -0,0 +1,40 @@ +export interface WithConfig { + appConfig: Config; +} + +export class Config { + public apiUrl?: string; + public clientId: string; + public clientKey: string; + public filter?: Record; + + // module config + public language?: string = 'en'; + public accession?: BaseFeatureConfig; + public shoppingCart?: BaseFeatureConfig; + + public constructor(config: Config) { + this.apiUrl = config.apiUrl || defaultConfig.apiUrl; + this.clientId = config.clientId; + this.clientKey = config.clientKey; + this.language = config.language || defaultConfig.language; + this.filter = config.filter || defaultConfig.filter; + + // Merge feature config + this.accession = { ...defaultConfig.accession, ...config.accession }; + this.shoppingCart = { ...defaultConfig.shoppingCart, ...config.shoppingCart }; + // console.log('Source and merged configuration', config, this); + } +} + +export class BaseFeatureConfig { + public enabled: boolean; +} + +export const defaultConfig: Partial = { + apiUrl: 'https://api.sandbox.genesys-pgr.org', + filter: {}, + language: 'en', + accession: { enabled: true }, + shoppingCart: { enabled: false }, +}; diff --git a/src/core/actions/appConfig.ts b/src/core/actions/appConfig.ts new file mode 100644 index 0000000000000000000000000000000000000000..cf254164fe89744b7a8612a2f316cc99785bf4e0 --- /dev/null +++ b/src/core/actions/appConfig.ts @@ -0,0 +1,7 @@ +import { RECEIVE_APP_CONFIG } from 'core/constants/appConfig'; +import { Config } from 'config/config'; + +export const setConfig = (config: Config) => ({ + type: RECEIVE_APP_CONFIG, + payload: config, +}); diff --git a/src/core/actions/decoding.ts b/src/core/actions/decoding.ts new file mode 100644 index 0000000000000000000000000000000000000000..7bc5362be98e4d5ee919e8a5d3df524771b0702b --- /dev/null +++ b/src/core/actions/decoding.ts @@ -0,0 +1,16 @@ +import { VocabularyService } from '@genesys/client/service'; +import { RECEIVE_COUNTRY_CODES_DECODED } from 'core/constants/decoding'; + +export const getCountryCodes = (lang: string) => (dispatch) => { + return VocabularyService + .decode3166Alpha3Terms(lang) + .then((codes) => { + dispatch({ + type: RECEIVE_COUNTRY_CODES_DECODED, + payload: codes, + }); + }) + .catch((e) => { + console.log('Loading country codes decoding failed: ', e); + }) +}; diff --git a/src/core/constants/appConfig.ts b/src/core/constants/appConfig.ts new file mode 100644 index 0000000000000000000000000000000000000000..662851514f5cf9881064e356f5d0ad3986d9f8b2 --- /dev/null +++ b/src/core/constants/appConfig.ts @@ -0,0 +1 @@ +export const RECEIVE_APP_CONFIG = 'core/appConfig/RECEIVE'; diff --git a/src/core/constants/decoding.ts b/src/core/constants/decoding.ts new file mode 100644 index 0000000000000000000000000000000000000000..62c6eca28315053a3b92377cedbe70165c28df5a --- /dev/null +++ b/src/core/constants/decoding.ts @@ -0,0 +1 @@ +export const RECEIVE_COUNTRY_CODES_DECODED = 'core/decoding/RECEIVE'; diff --git a/src/core/reducer/appConfig.ts b/src/core/reducer/appConfig.ts new file mode 100644 index 0000000000000000000000000000000000000000..baa53c6265d49fefba1a8315f5593e4820525597 --- /dev/null +++ b/src/core/reducer/appConfig.ts @@ -0,0 +1,26 @@ +import update from 'immutability-helper'; + +import { RECEIVE_APP_CONFIG } from 'core/constants/appConfig'; +import { Config } from 'config/config'; + +const INITIAL_STATE: { + config: Config, +} = { + config: null, +}; + +export default (state = INITIAL_STATE, action: { type?: string, payload?: any } = { type: '', payload: {} }) => { + + switch (action.type) { + case RECEIVE_APP_CONFIG: { + const config = new Config(action.payload); + + return update(state, { + config: { $set: config }, + }); + } + + default: + return state; + } +} diff --git a/src/core/reducer/decoding.ts b/src/core/reducer/decoding.ts new file mode 100644 index 0000000000000000000000000000000000000000..94dfa355d0a2e29d337761a9ad911c1e160a9174 --- /dev/null +++ b/src/core/reducer/decoding.ts @@ -0,0 +1,23 @@ +import update from 'immutability-helper'; + +import { RECEIVE_COUNTRY_CODES_DECODED } from 'core/constants/decoding'; + +const INITIAL_STATE: { + countryCodes: Record, +} = { + countryCodes: null, +}; + +export default (state = INITIAL_STATE, action: { type?: string, payload?: any } = { type: '', payload: {} }) => { + + switch (action.type) { + case RECEIVE_COUNTRY_CODES_DECODED: { + return update(state, { + countryCodes: { $set: action.payload }, + }); + } + + default: + return state; + } +} diff --git a/src/core/reducer/index.ts b/src/core/reducer/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..8256d9e1f69d7347e6ee084868953b21510e08b1 --- /dev/null +++ b/src/core/reducer/index.ts @@ -0,0 +1,11 @@ +import { combineReducers } from 'redux'; +import appConfig from 'core/reducer/appConfig'; +import decoding from './decoding'; + +const rootReducer = () => (combineReducers({ + appConfig, + decoding, +})); + + +export default rootReducer; diff --git a/src/genesys.tsx b/src/genesys.tsx index 4a4690a82737dc1b0436d9eec19d5006ecafdd10..ff46ba6d53b257246a67a5e55f34de1c06628b87 100644 --- a/src/genesys.tsx +++ b/src/genesys.tsx @@ -1,16 +1,24 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import initI18n from './i18n'; +import { applyMiddleware, compose, createStore } from 'redux'; import { log } from '@genesys/client/utilities/debug'; +import thunk from 'redux-thunk'; +import { Provider } from 'react-redux'; // import * as cookies from 'es-cookie'; import { reconfigureServiceAxios, LoginService } from '@genesys/client/service'; import App from 'ui/core/App'; import ApiAccessError from 'ui/core/ApiAccessError'; -import { Config, DefaultConfig } from '../config/config'; +import { Config } from 'config/config'; +import rootReducer from 'core/reducer'; +import { setConfig } from 'core/actions/appConfig'; import OverviewPage from 'accession/OverviewPage'; -// declare const window: Window & { devToolsExtension: any, __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: any, initialLanguage: any, initialI18nStore: any, localeMapping: any }; +declare const window: Window & { devToolsExtension: any, __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: any }; +const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; +const initialState = {}; +const store = composeEnhancers(applyMiddleware(thunk))(createStore)(rootReducer(), initialState); // const AUTH_COOKIE = 'GENESYS_AUTH'; console.log('Loading Genesys UI library'); @@ -43,16 +51,19 @@ function checkAccessTokens(apiUrl: string, clientId: string, clientSecret: strin return appLogin; }; -export function showGenesysUI(holdingNode: HTMLElement, config: Config = DefaultConfig, language: string = 'en') { - const { apiUrl, clientId, clientKey, filter } = config; +export function showGenesysUI(holdingNode: HTMLElement, config: Config) { + const { apiUrl, clientId, clientKey, language } = config; reconfigureServiceAxios({ apiUrl }); initI18n(language); + store.dispatch(setConfig(config)); checkAccessTokens(apiUrl, clientId, clientKey).then( (result) => { ReactDOM.render( - , + + + , holdingNode, ); }).catch((err) => { @@ -65,16 +76,19 @@ export function showGenesysUI(holdingNode: HTMLElement, config: Config = Default } -export function showOverview(holdingNode: HTMLElement, config: Config = DefaultConfig, language: string = 'en') { - const { apiUrl, clientId, clientKey, filter } = config; +export function showOverview(holdingNode: HTMLElement, config: Config) { + const { apiUrl, clientId, clientKey, language } = config; reconfigureServiceAxios({ apiUrl }); initI18n(language); + store.dispatch(setConfig(config)); checkAccessTokens(apiUrl, clientId, clientKey).then( (result) => { ReactDOM.render( - , + + + , holdingNode, ); }).catch((err) => { diff --git a/src/ui/core/App.tsx b/src/ui/core/App.tsx index c30dde933f3b1bc39a7eff0ad2867fc5881a66f4..8fc00a792c15319de15bd7959561da6a513e0722 100644 --- a/src/ui/core/App.tsx +++ b/src/ui/core/App.tsx @@ -1,10 +1,15 @@ import React from 'react'; +import { connect } from 'react-redux'; import { HashRouter as Router, Route, Switch } from 'react-router-dom'; import NotFound from 'ui/core/NotFound'; import ApiInfoPage from 'serverinfo/ApiInfoPage'; import AccessionList from 'accession/AccessionListPage'; import AccessionDetails from 'accession/AccessionDetailsPage'; import { createHashHistory } from 'history'; +import { bindActionCreators } from 'redux'; +import { getCountryCodes } from 'core/actions/decoding'; +import { withTranslation, WithTranslation } from 'react-i18next'; +import { WithConfig } from 'config/config'; import CartPage from 'request/CartPage'; import OverviewPage from 'accession/OverviewPage'; import RequestPage from 'request/RequestPage'; @@ -12,25 +17,29 @@ import RequestPage from 'request/RequestPage'; const hashHistory = createHashHistory({}); interface IAppProps extends React.ClassAttributes { - apiUrl: string; - filter: object; + getCountryCodes: (lang: string) => Promise>; } -export default class App extends React.Component { +class App extends React.Component { + public componentDidMount() { + const { getCountryCodes, i18n } = this.props; + getCountryCodes(i18n.language); + } + public render() { - const { apiUrl } = this.props; - console.log('Using filter', this.props.filter); + const { appConfig: { apiUrl, shoppingCart } } = this.props; + return ( <>
- } /> - }/> + + - - }/> - + { shoppingCart.enabled && } + + { shoppingCart.enabled && } @@ -45,4 +54,14 @@ export default class App extends React.Component { }; } +const mapStateToProps = (state) => ({ + appConfig: state.appConfig.config, +}); + +const mapDispatchToProps = (dispatch) => bindActionCreators({ + getCountryCodes, +}, dispatch); + +export default connect(mapStateToProps, mapDispatchToProps)(withTranslation()(App)); + export { hashHistory as history }; diff --git a/yarn.lock b/yarn.lock index e6da51b6e106ad2bed037fb72e5ebd6e6ef7a80b..1d9436810b5e022c4695d60e5b449a110a1372a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -72,9 +72,9 @@ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*": - version "14.14.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.8.tgz#2127bd81949a95c8b7d3240f3254352d72563aec" - integrity sha512-z/5Yd59dCKI5kbxauAJgw6dLPzW+TNOItNE00PkpzNwUIEwdj/Lsqwq94H5DdYBX7C13aRA0CY32BK76+neEUA== + version "14.14.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.9.tgz#04afc9a25c6ff93da14deabd65dc44485b53c8d6" + integrity sha512-JsoLXFppG62tWTklIoO4knA+oDTYsmqWxHRvd4lpmfQRNhX6osheUOWETP2jMoV/2bEHuMra8Pp3Dmo/stBFcw== "@types/prop-types@*": version "15.7.3" @@ -104,9 +104,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^16.0.0": - version "16.9.56" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.56.tgz#ea25847b53c5bec064933095fc366b1462e2adf0" - integrity sha512-gIkl4J44G/qxbuC6r2Xh+D3CGZpJ+NdWTItAPmZbR5mUS+JQ8Zvzpl0ea5qT/ZT3ZNTUcDKUVqV3xBE8wv/DyQ== + version "16.14.0" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.0.tgz#bc022cfe1609899b0f2196376b267724e7300f0e" + integrity sha512-jJjHo1uOe+NENRIBvF46tJimUvPnmbQ41Ax0pEm7pRvhPg+wuj8VMOHHiMvaGmZRzRrCtm7KnL5OOE/6kHPK8w== dependencies: "@types/prop-types" "*" csstype "^3.0.2" @@ -597,9 +597,9 @@ awesome-typescript-loader@^5.0.0: webpack-log "^1.2.0" axe-core@^4.0.2: - version "4.1.0" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.1.0.tgz#93d395e6262ecdde5cb52a5d06533d0a0c7bb4cd" - integrity sha512-9atDIOTDLsWL+1GbBec6omflaT5Cxh88J0GtJtGfCVIXpI02rXHkju59W5mMqWa7eiC5OR168v3TK3kUKBW98g== + version "4.1.1" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.1.1.tgz#70a7855888e287f7add66002211a423937063eaf" + integrity sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ== axobject-query@^2.2.0: version "2.2.0" @@ -2064,9 +2064,9 @@ ejs@^2.6.1: integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== electron-to-chromium@^1.3.591: - version "1.3.601" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.601.tgz#881824eaef0b2f97c89e1abb5835fdd224997d34" - integrity sha512-ctRyXD9y0mZu8pgeNwBUhLP3Guyr5YuqkfLKYmpTwYx7o9JtCEJme9JVX4xBXPr5ZNvr/iBXUvHLFEVJQThATg== + version "1.3.603" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.603.tgz#1b71bec27fb940eccd79245f6824c63d5f7e8abf" + integrity sha512-J8OHxOeJkoSLgBXfV9BHgKccgfLMHh+CoeRo6wJsi6m0k3otaxS/5vrHpMNSEYY4MISwewqanPOuhAtuE8riQQ== elliptic@^6.5.3: version "6.5.3" @@ -3093,7 +3093,7 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -3292,6 +3292,11 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +immutability-helper@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-3.1.1.tgz#2b86b2286ed3b1241c9e23b7b21e0444f52f77b7" + integrity sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ== + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -5271,11 +5276,22 @@ react-i18next@^11.0.0: "@babel/runtime" "^7.3.1" html-parse-stringify2 "2.0.1" -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-redux@^7.0.0: + version "7.2.2" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.2.tgz#03862e803a30b6b9ef8582dadcc810947f74b736" + integrity sha512-8+CQ1EvIVFkYL/vu6Olo7JFLWop1qRUeb46sGtIMDCSpgwPQq8fPLpirIB0iTqFe9XYEFPHssdX8/UwN6pAkEA== + dependencies: + "@babel/runtime" "^7.12.1" + hoist-non-react-statics "^3.3.2" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.13.1" + react-router-dom@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" @@ -5369,6 +5385,24 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" +redux-devtools-extension@^2.13.8: + version "2.13.8" + resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1" + integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg== + +redux-thunk@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" + integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== + +redux@^4.0.0: + version "4.0.5" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" + integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== + dependencies: + loose-envify "^1.4.0" + symbol-observable "^1.2.0" + regenerator-runtime@^0.10.5: version "0.10.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" @@ -5778,7 +5812,7 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -side-channel@^1.0.2: +side-channel@^1.0.2, side-channel@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3" integrity sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g== @@ -6046,16 +6080,17 @@ string-width@^4.1.0: strip-ansi "^6.0.0" string.prototype.matchall@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz#48bb510326fb9fdeb6a33ceaa81a6ea04ef7648e" - integrity sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg== + version "4.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.3.tgz#24243399bc31b0a49d19e2b74171a15653ec996a" + integrity sha512-OBxYDA2ifZQ2e13cP82dWFMaCV9CGF8GzmN4fljBVw5O5wep0lu4gacm1OL6MjROoUnB8VbkWRThqkV2YFLNxw== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.17.0" + es-abstract "^1.18.0-next.1" has-symbols "^1.0.1" internal-slot "^1.0.2" regexp.prototype.flags "^1.3.0" - side-channel "^1.0.2" + side-channel "^1.0.3" string.prototype.trimend@^1.0.1: version "1.0.2" @@ -6172,6 +6207,11 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.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"