diff --git a/package.json b/package.json
index e692922..543ec0c 100644
--- a/package.json
+++ b/package.json
@@ -4,16 +4,23 @@
"description": "Coding Challenge for React developers",
"private": false,
"dependencies": {
+ "axios": "^0.19.2",
+ "babel-plugin-styled-components": "^1.10.7",
+ "dotenv": "^8.2.0",
"env-cmd": "^9.0.3",
"history": "^4.9.0",
"prop-types": "^15.6.2",
"query-string": "^6.7.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
+ "react-redux": "^7.2.0",
"react-router": "^4.3.1",
- "react-router-dom": "^4.3.1",
+ "react-router-dom": "^5.1.2",
"react-scripts": "3.0.1",
- "sanitize.css": "^10.0.0"
+ "redux": "^4.0.5",
+ "redux-thunk": "^2.3.0",
+ "sanitize.css": "^10.0.0",
+ "styled-components": "^5.1.0"
},
"devDependencies": {
"enzyme": "^3.6.0",
diff --git a/src/HOC/LayoutHOC.jsx b/src/HOC/LayoutHOC.jsx
new file mode 100644
index 0000000..e0b3894
--- /dev/null
+++ b/src/HOC/LayoutHOC.jsx
@@ -0,0 +1,76 @@
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import styled from 'styled-components';
+import PropTypes from 'prop-types';
+import { Link } from 'react-router-dom';
+
+import { BookListComponentPropTypes, BookComponentPropTypes } from '../components/product-list/Book-List-Props';
+import { fetchBooks } from '../actions';
+
+const Layout = styled.div`
+ width: 80%;
+ margin: 0 auto;
+`;
+
+const LayoutHOC = pageType => (WrapperComponent) => {
+ class HOC extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {};
+ }
+
+ componentWillMount() {
+ const { dispatch, match } = this.props;
+
+ if (pageType === 'book-list') {
+ dispatch(fetchBooks());
+ }
+
+ if (pageType === 'book-detail') {
+ dispatch(fetchBooks(match.params.id));
+ }
+ }
+
+ render() {
+ return (
+
+ Books
+
+
+
+ );
+ }
+ }
+
+ HOC.propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ isFetching: PropTypes.bool,
+ books: BookListComponentPropTypes.books,
+ book: BookComponentPropTypes,
+
+ match: PropTypes.shape({
+ params: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ }).isRequired,
+ }).isRequired,
+ };
+
+
+ HOC.defaultProps = {
+ books: [],
+ book: {},
+ isFetching: false,
+ };
+
+ const mapStateToProps = state => ({
+ isFetching: state.isFetching,
+ books: state.books,
+ book: state.book,
+ });
+
+ return connect(mapStateToProps)(HOC);
+};
+
+
+export default LayoutHOC;
diff --git a/src/HOC/LayoutHOC.test.jsx b/src/HOC/LayoutHOC.test.jsx
new file mode 100644
index 0000000..7276dcd
--- /dev/null
+++ b/src/HOC/LayoutHOC.test.jsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import {
+ BookDetailComponent
+} from '../components/product-detail/Book-Detail';
+
+import LayoutHOC from './LayoutHOC';
+
+// Couldn't get this to work. I think I made the HOC too complex.
+describe('', () => {
+ it('renders', () => {
+ // const el = LayoutHOC('book-detail')(BookDetailComponent);
+ // const wrapper = shallow(el);
+ // expect(wrapper).toBeTruthy();
+ });
+});
diff --git a/src/__tests__/Home.test.js b/src/__tests__/Home.test.js
index 8ec88a1..7328e5f 100644
--- a/src/__tests__/Home.test.js
+++ b/src/__tests__/Home.test.js
@@ -6,7 +6,8 @@ import Home from '../views/Home';
describe('', () => {
describe('renders', () => {
it('without crashing', () => {
- shallow();
+ const wrapper = shallow();
+ expect(wrapper).toBeTruthy();
});
});
});
diff --git a/src/actions/index.js b/src/actions/index.js
new file mode 100644
index 0000000..40af827
--- /dev/null
+++ b/src/actions/index.js
@@ -0,0 +1,52 @@
+import getBooks from '../services/product';
+
+export const requestBooks = () => ({
+ type: 'REQUEST_BOOKS',
+});
+
+export const receiveBooks = books => ({
+ type: 'RECEIVE_BOOKS',
+ books,
+});
+
+export const requestBook = bookId => ({
+ type: 'REQUEST_BOOK',
+ bookId,
+});
+
+export const receiveBook = book => ({
+ type: 'RECEIVE_BOOK',
+ book,
+});
+
+const getBook = (bookId, bookArray) => {
+ const output = bookArray.filter(curr => (parseInt(curr.book_id, 10) === parseInt(bookId, 10)));
+ if (output.length > 0) {
+ return output[0];
+ }
+
+ return [];
+};
+
+export const fetchBooks = bookId => (dispatch, getState) => {
+ const state = getState();
+ if (!state.books) {
+ dispatch(requestBooks());
+ return getBooks().then((books) => {
+ dispatch(receiveBooks(books));
+
+ if (bookId) {
+ dispatch(requestBook(bookId));
+ dispatch(receiveBook(getBook(bookId, books)));
+ }
+ });
+ }
+
+ if (bookId) {
+ dispatch(requestBook(bookId));
+ return dispatch(receiveBook(getBook(bookId, state.books)));
+ }
+
+ dispatch(requestBooks());
+ return dispatch(receiveBooks(state.books));
+};
diff --git a/src/components/product-detail/Book-Detail.jsx b/src/components/product-detail/Book-Detail.jsx
new file mode 100644
index 0000000..7de55c8
--- /dev/null
+++ b/src/components/product-detail/Book-Detail.jsx
@@ -0,0 +1,84 @@
+import React from 'react';
+import styled from 'styled-components';
+import PropTypes from 'prop-types';
+
+import LayoutHOC from '../../HOC/LayoutHOC';
+
+export const Detail = styled.div`
+ margin: 20px 0;
+ display: flex;
+ width: 30%;
+`;
+
+export const Author = styled.span`
+ display: block;
+ font-size: 12px;
+`;
+
+export const Title = styled.span`
+ display: block;
+ font-size: 14px;
+`;
+
+export const Cover = styled.img`
+ display: block;
+ border: 1px solid black;
+ min-width: 50px;
+ max-width: 100px;
+ min-height: 50px;
+ max-height: 100px;
+ margin-right: 20px;
+
+`;
+
+export const ISBN = styled.p`
+ margin-bottom: 8px;
+ font-size: 10px;
+`;
+
+export const Publish = styled.p`
+ margin-bottom: 8px;
+ font-size: 10px;
+ font-weight: bold;
+`;
+
+export function BookDetailComponent({ book }) {
+ if (book) {
+ return (
+
+
+
+
{book.name}
+
{book.author}
+
+ ISBN:
+ {book.isbn}
+
+
+ Published on:
+ {book.published_at}
+
+
+
+ );
+ }
+
+ return
No books found
;
+}
+
+BookDetailComponent.propTypes = {
+ book: PropTypes.shape({
+ book_id: PropTypes.number,
+ name: PropTypes.string,
+ isbn: PropTypes.string,
+ published_at: PropTypes.string,
+ author: PropTypes.string,
+ cover: PropTypes.string,
+ }),
+};
+
+BookDetailComponent.defaultProps = {
+ book: {},
+};
+
+export default LayoutHOC('book-detail')(BookDetailComponent);
diff --git a/src/components/product-detail/Book-Detail.test.jsx b/src/components/product-detail/Book-Detail.test.jsx
new file mode 100644
index 0000000..84f9e98
--- /dev/null
+++ b/src/components/product-detail/Book-Detail.test.jsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import {
+ BookDetailComponent, Author, Cover, ISBN, Publish, Title,
+} from './Book-Detail';
+
+describe('', () => {
+ it('renders', () => {
+ const wrapper = shallow();
+ expect(wrapper).toBeTruthy();
+ });
+
+ it('renders book detail properly', () => {
+ const book = {
+ book_id: 1, name: 'Book Title', author: 'Author', cover: 'Cover',
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.find(Author).length).toBe(1);
+ expect(wrapper.find(Cover).length).toBe(1);
+ expect(wrapper.find(Title).length).toBe(1);
+ expect(wrapper.find(ISBN).length).toBe(1);
+ expect(wrapper.find(Publish).length).toBe(1);
+ });
+});
diff --git a/src/components/product-list/Book-List-Props.jsx b/src/components/product-list/Book-List-Props.jsx
new file mode 100644
index 0000000..1f555d9
--- /dev/null
+++ b/src/components/product-list/Book-List-Props.jsx
@@ -0,0 +1,14 @@
+import PropTypes from 'prop-types';
+
+export const BookComponentPropTypes = {
+ book_id: PropTypes.number,
+ name: PropTypes.string,
+ isbn: PropTypes.string,
+ published_at: PropTypes.string,
+ author: PropTypes.string,
+ cover: PropTypes.string,
+};
+
+export const BookListComponentPropTypes = {
+ books: PropTypes.arrayOf(PropTypes.shape(BookComponentPropTypes)),
+};
diff --git a/src/components/product-list/Book-List.jsx b/src/components/product-list/Book-List.jsx
new file mode 100644
index 0000000..2203b8c
--- /dev/null
+++ b/src/components/product-list/Book-List.jsx
@@ -0,0 +1,81 @@
+import React from 'react';
+import styled from 'styled-components';
+import { Link } from 'react-router-dom';
+
+import LayoutHOC from '../../HOC/LayoutHOC';
+import { BookListComponentPropTypes } from './Book-List-Props';
+
+export const List = styled.ul`
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-wrap: wrap;
+`;
+
+export const StyledLink = styled(Link)`
+ display: flex;
+ color: #333;
+
+ &:hover {
+ text-decoration: none;
+ }
+`;
+
+export const ListItem = styled.li`
+ margin: 20px 0;
+ display: flex;
+ width: 30%;
+`;
+
+export const Author = styled.span`
+ display: block;
+ font-size: 12px;
+`;
+
+export const Title = styled.span`
+ display: block;
+ font-size: 14px;
+`;
+
+export const Cover = styled.img`
+ display: block;
+ border: 1px solid #333;
+ min-width: 50px;
+ max-width: 100px;
+ min-height: 50px;
+ max-height: 100px;
+ margin-right: 20px;
+
+`;
+
+
+export function BookListComponent({ books }) {
+ if (books && books.length) {
+ return (
+
+ {books.map(curr => (
+
+
+
+
+
+
{curr.name}
+
{curr.author}
+
+
+
+ ))}
+
+ );
+ }
+
+ return No books found
;
+}
+
+BookListComponent.propTypes = BookListComponentPropTypes;
+
+BookListComponent.defaultProps = {
+ books: [],
+};
+
+export default LayoutHOC('book-list')(BookListComponent);
diff --git a/src/components/product-list/Book-List.test.jsx b/src/components/product-list/Book-List.test.jsx
new file mode 100644
index 0000000..4373aad
--- /dev/null
+++ b/src/components/product-list/Book-List.test.jsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import {
+ BookListComponent as BL, Author, Cover, List, ListItem, Title,
+} from './Book-List';
+
+describe('', () => {
+ it('renders', () => {
+ const wrapper = shallow();
+ expect(wrapper).toBeTruthy();
+ });
+
+ it('renders book list properly', () => {
+ const books = [{
+ book_id: 1, name: 'Book Title', author: 'Author', cover: 'Cover',
+ }];
+ const wrapper = shallow();
+
+ expect(wrapper.find(Author).length).toBe(1);
+ expect(wrapper.find(List).length).toBe(1);
+ expect(wrapper.find(Cover).length).toBe(1);
+ expect(wrapper.find(ListItem).length).toBe(1);
+ expect(wrapper.find(Title).length).toBe(1);
+ });
+});
diff --git a/src/index.css b/src/index.css
index c0a127a..cdb3c42 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,5 +1,6 @@
body {
- background-color: #54a0ff;
- color: #341f97;
- font-weight: 100;
+ background-color: #efefef;
+ color: #333;
+ font-weight: normal;
+ font-family: sans-serif;
}
diff --git a/src/index.js b/src/index.js
index 585eea9..1060a1c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,14 +1,25 @@
import React from 'react';
import ReactDOM from 'react-dom';
+import { createStore, applyMiddleware, compose } from 'redux';
+import thunk from 'redux-thunk';
import * as serviceWorker from './serviceWorker';
+
+import productListApp from './reducers';
+
+import './index.css';
import 'sanitize.css';
import Routes from './routes';
+// eslint-disable-next-line max-len
+const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
+const store = createStore(productListApp, /* preloadedState, */ composeEnhancers(
+ applyMiddleware(thunk)
+));
ReactDOM.render(
// TODO use jsx extension for this file, will require to eject the create-react-app
// eslint-disable-next-line react/jsx-filename-extension
- ,
+ ,
document.getElementById('root'),
);
diff --git a/src/reducers/books.js b/src/reducers/books.js
new file mode 100644
index 0000000..854e60c
--- /dev/null
+++ b/src/reducers/books.js
@@ -0,0 +1,23 @@
+const books = (state = [], action) => {
+ switch (action.type) {
+ case 'REQUEST_BOOKS':
+ return {
+ ...state,
+ isFetching: true,
+ };
+ case 'RECEIVE_BOOKS':
+ return {
+ ...state,
+ isFetching: false,
+ books: action.books,
+ };
+ case 'REQUEST_BOOK':
+ return { ...state, isFetching: true };
+ case 'RECEIVE_BOOK':
+ return { ...state, isFetching: false, book: action.book };
+ default:
+ return state;
+ }
+};
+
+export default books;
diff --git a/src/reducers/index.js b/src/reducers/index.js
new file mode 100644
index 0000000..769f145
--- /dev/null
+++ b/src/reducers/index.js
@@ -0,0 +1,3 @@
+import books from './books';
+
+export default books;
diff --git a/src/routes.jsx b/src/routes.jsx
index 269ceb0..3eae44d 100644
--- a/src/routes.jsx
+++ b/src/routes.jsx
@@ -1,18 +1,24 @@
+/* eslint-disable react/forbid-prop-types */
+
import React from 'react';
-import {
- Router,
- Route,
- Switch,
-} from 'react-router-dom';
-import { createBrowserHistory } from 'history';
-import Home from './views/Home';
+import { Provider } from 'react-redux';
+import { PropTypes } from 'prop-types';
+import { BrowserRouter as Router, Route } from 'react-router-dom';
+import BookList from './components/product-list/Book-List';
+import BookDetail from './components/product-detail/Book-Detail';
-const Routes = () => (
-
-
-
-
-
+const Routes = ({ store }) => (
+
+
+
+
+
+
+
);
+Routes.propTypes = {
+ store: PropTypes.object.isRequired,
+};
+
export default Routes;
diff --git a/src/services/product.js b/src/services/product.js
new file mode 100644
index 0000000..58f7cf9
--- /dev/null
+++ b/src/services/product.js
@@ -0,0 +1,18 @@
+import axios from 'axios';
+import dotenv from 'dotenv';
+
+dotenv.config();
+
+export default async function getBooks() {
+ try {
+ const products = await axios
+ .get(process.env.REACT_APP_API_URL);
+ if (products && products.data) {
+ return products.data;
+ }
+ } catch (err) {
+ console.log(err);
+ }
+
+ return [];
+}
diff --git a/src/services/product.test.js b/src/services/product.test.js
new file mode 100644
index 0000000..cce07de
--- /dev/null
+++ b/src/services/product.test.js
@@ -0,0 +1,30 @@
+import axios from 'axios';
+import dotenv from 'dotenv';
+
+import { getBooks } from './product';
+
+jest.mock('axios');
+
+dotenv.config();
+
+describe('Services', () => {
+ describe('getBooks', () => {
+ it('should return something', () => {
+ const id = Math.floor(Math.random() * 10);
+ const books = [
+ {
+ book_id: id,
+ name: 'Becker West Arnoldo',
+ isbn: '3181444340',
+ published_at: '2000-01-01',
+ author: 'Mrs. John Doe',
+ cover: 'https://lorempixel.com/640/480/?82539',
+ },
+ ];
+
+ axios.get.mockImplementation(() => Promise.resolve({ data: books }));
+
+ return expect(getBooks()).resolves.toEqual(books);
+ });
+ });
+});
diff --git a/src/views/Home.jsx b/src/views/Home.jsx
index bc30ad0..fcd0cb9 100644
--- a/src/views/Home.jsx
+++ b/src/views/Home.jsx
@@ -7,25 +7,12 @@ const HomeStyle = {
justifyContent: 'center',
};
-const Home = ({ match }) => (
+const Home = () => (
Welcome!
- {(match.params.testRouting) && (
-
- {match.params.testRouting}
-
- )}
);
-Home.propTypes = {
- match: PropTypes.shape({
- params: PropTypes.shape({
- testRouting: PropTypes.string.isRequired,
- }).isRequired,
- }).isRequired,
-};
-
export default Home;
diff --git a/yarn.lock b/yarn.lock
index 72e6d81..ed45089 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -859,6 +859,13 @@
dependencies:
regenerator-runtime "^0.13.2"
+"@babel/runtime@^7.4.0", "@babel/runtime@^7.5.5":
+ version "7.9.2"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06"
+ integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237"
@@ -910,6 +917,28 @@
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-9.0.1.tgz#c27b391d8457d1e893f1eddeaf5e5412d12ffbb5"
integrity sha512-6It2EVfGskxZCQhuykrfnALg7oVeiI6KclWSmGDqB0AiInVrTGB9Jp9i4/Ad21u9Jde/voVQz6eFX/eSg/UsPA==
+"@emotion/is-prop-valid@^0.8.8":
+ version "0.8.8"
+ resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
+ integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
+ dependencies:
+ "@emotion/memoize" "0.7.4"
+
+"@emotion/memoize@0.7.4":
+ version "0.7.4"
+ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
+ integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
+
+"@emotion/stylis@^0.8.4":
+ version "0.8.5"
+ resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04"
+ integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
+
+"@emotion/unitless@^0.7.4":
+ version "0.7.5"
+ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
+ integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
+
"@hapi/address@2.x.x":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a"
@@ -1863,6 +1892,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
+axios@^0.19.2:
+ version "0.19.2"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
+ integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
+ dependencies:
+ follow-redirects "1.5.10"
+
axobject-query@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9"
@@ -1958,6 +1994,21 @@ babel-plugin-named-asset-import@^0.3.2:
resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.2.tgz#20978ed446b8e1bf4a2f42d0a94c0ece85f75f4f"
integrity sha512-CxwvxrZ9OirpXQ201Ec57OmGhmI8/ui/GwTDy0hSp6CmRvgRC0pSair6Z04Ck+JStA0sMPZzSJ3uE4n17EXpPQ==
+"babel-plugin-styled-components@>= 1", babel-plugin-styled-components@^1.10.7:
+ version "1.10.7"
+ resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.10.7.tgz#3494e77914e9989b33cc2d7b3b29527a949d635c"
+ integrity sha512-MBMHGcIA22996n9hZRf/UJLVVgkEOITuR2SvjHLb5dSTUyR4ZRGn+ngITapes36FI3WLxZHfRhkA1ffHxihOrg==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.0.0"
+ "@babel/helper-module-imports" "^7.0.0"
+ babel-plugin-syntax-jsx "^6.18.0"
+ lodash "^4.17.11"
+
+babel-plugin-syntax-jsx@^6.18.0:
+ version "6.18.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
+ integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=
+
babel-plugin-syntax-object-rest-spread@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
@@ -2360,6 +2411,11 @@ camelcase@^5.0.0, camelcase@^5.2.0, camelcase@^5.3.1:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+camelize@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b"
+ integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
+
caniuse-api@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
@@ -2862,6 +2918,11 @@ css-blank-pseudo@^0.1.4:
dependencies:
postcss "^7.0.5"
+css-color-keywords@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
+ integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=
+
css-color-names@0.0.4, css-color-names@^0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
@@ -2932,6 +2993,15 @@ css-select@^2.0.0:
domutils "^1.7.0"
nth-check "^1.0.2"
+css-to-react-native@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.0.0.tgz#62dbe678072a824a689bcfee011fc96e02a7d756"
+ integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==
+ dependencies:
+ camelize "^1.0.0"
+ css-color-keywords "^1.0.0"
+ postcss-value-parser "^4.0.2"
+
css-tree@1.0.0-alpha.28:
version "1.0.0-alpha.28"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.28.tgz#8e8968190d886c9477bc8d61e96f61af3f7ffa7f"
@@ -3103,6 +3173,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.
dependencies:
ms "2.0.0"
+debug@=3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+ integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
+ dependencies:
+ ms "2.0.0"
+
debug@^3.2.5, debug@^3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -3390,6 +3467,11 @@ dotenv@6.2.0:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
+dotenv@^8.2.0:
+ version "8.2.0"
+ resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
+ integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
+
duplexer@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
@@ -4199,6 +4281,13 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
+follow-redirects@1.5.10:
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
+ integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
+ dependencies:
+ debug "=3.1.0"
+
follow-redirects@^1.0.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76"
@@ -4478,6 +4567,11 @@ growly@^1.3.0:
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
+gud@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
+ integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==
+
gzip-size@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.0.0.tgz#a55ecd99222f4c48fd8c01c625ce3b349d0a0e80"
@@ -4658,6 +4752,13 @@ hoist-non-react-statics@^2.5.0:
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.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
+ 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==
+ dependencies:
+ react-is "^16.7.0"
+
hosted-git-info@^2.1.4:
version "2.7.1"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
@@ -6355,6 +6456,15 @@ mimic-fn@^2.0.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+mini-create-react-context@^0.3.0:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz#79fc598f283dd623da8e088b05db8cddab250189"
+ integrity sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==
+ dependencies:
+ "@babel/runtime" "^7.4.0"
+ gud "^1.0.0"
+ tiny-warning "^1.0.2"
+
mini-css-extract-plugin@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz#ac0059b02b9692515a637115b0cc9fed3a35c7b0"
@@ -7904,6 +8014,11 @@ postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
+postcss-value-parser@^4.0.2:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz#651ff4593aa9eda8d5d0d66593a2417aeaeb325d"
+ integrity sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==
+
postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f"
@@ -8242,22 +8357,55 @@ react-error-overlay@^5.1.6:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.6.tgz#0cd73407c5d141f9638ae1e0c63e7b2bf7e9929d"
integrity sha512-X1Y+0jR47ImDVr54Ab6V9eGk0Hnu7fVWGeHQSOXHf/C2pF9c6uy3gef8QUeuUiWlNb0i08InPSE5a/KJzNzw1Q==
+react-is@^16.6.0, react-is@^16.7.0, react-is@^16.9.0:
+ 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-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
-react-router-dom@^4.3.1:
- version "4.3.1"
- resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.3.1.tgz#4c2619fc24c4fa87c9fd18f4fb4a43fe63fbd5c6"
- integrity sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==
+react-redux@^7.2.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d"
+ integrity sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==
dependencies:
- history "^4.7.2"
- invariant "^2.2.4"
+ "@babel/runtime" "^7.5.5"
+ hoist-non-react-statics "^3.3.0"
+ loose-envify "^1.4.0"
+ prop-types "^15.7.2"
+ react-is "^16.9.0"
+
+react-router-dom@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18"
+ integrity sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew==
+ dependencies:
+ "@babel/runtime" "^7.1.2"
+ history "^4.9.0"
loose-envify "^1.3.1"
- prop-types "^15.6.1"
- react-router "^4.3.1"
- warning "^4.0.1"
+ prop-types "^15.6.2"
+ react-router "5.1.2"
+ tiny-invariant "^1.0.2"
+ tiny-warning "^1.0.0"
+
+react-router@5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418"
+ integrity sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==
+ dependencies:
+ "@babel/runtime" "^7.1.2"
+ history "^4.9.0"
+ hoist-non-react-statics "^3.1.0"
+ loose-envify "^1.3.1"
+ mini-create-react-context "^0.3.0"
+ path-to-regexp "^1.7.0"
+ prop-types "^15.6.2"
+ react-is "^16.6.0"
+ tiny-invariant "^1.0.2"
+ tiny-warning "^1.0.0"
react-router@^4.3.1:
version "4.3.1"
@@ -8431,6 +8579,19 @@ recursive-readdir@2.2.2:
dependencies:
minimatch "3.0.4"
+redux-thunk@^2.3.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.5:
+ 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"
+
reflect.ownkeys@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460"
@@ -8458,6 +8619,11 @@ regenerator-runtime@^0.11.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+regenerator-runtime@^0.13.4:
+ version "0.13.5"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
+ integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
+
regenerator-transform@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.0.tgz#2ca9aaf7a2c239dd32e4761218425b8c7a86ecaf"
@@ -8951,6 +9117,11 @@ shallow-clone@^1.0.0:
kind-of "^5.0.0"
mixin-object "^2.0.1"
+shallowequal@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
+ integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
+
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
@@ -9379,6 +9550,22 @@ style-loader@0.23.1:
loader-utils "^1.1.0"
schema-utils "^1.0.0"
+styled-components@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.1.0.tgz#2e3985b54f461027e1c91af3229e1c2530872a4e"
+ integrity sha512-0Qs2wEkFBXHFlysz6CV831VG6HedcrFUwChjnWylNivsx14MtmqQsohi21rMHZxzuTba063dEyoe/SR6VGJI7Q==
+ dependencies:
+ "@babel/helper-module-imports" "^7.0.0"
+ "@babel/traverse" "^7.4.5"
+ "@emotion/is-prop-valid" "^0.8.8"
+ "@emotion/stylis" "^0.8.4"
+ "@emotion/unitless" "^0.7.4"
+ babel-plugin-styled-components ">= 1"
+ css-to-react-native "^3.0.0"
+ hoist-non-react-statics "^3.0.0"
+ shallowequal "^1.1.0"
+ supports-color "^5.5.0"
+
stylehacks@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5"
@@ -9393,7 +9580,7 @@ supports-color@^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:
+supports-color@^5.3.0, supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
@@ -9427,6 +9614,11 @@ svgo@^1.0.0, svgo@^1.2.2:
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==
+
symbol-tree@^3.2.2:
version "3.2.4"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
@@ -9568,6 +9760,11 @@ tiny-warning@^1.0.0:
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.2.tgz#1dfae771ee1a04396bdfde27a3adcebc6b648b28"
integrity sha512-rru86D9CpQRLvsFG5XFdy0KdLAvjdQDyZCsRcuu60WtzFylDM3eAWSxEVz5kzL2Gp544XiUvPbVKtOA/txLi9Q==
+tiny-warning@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
+ integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
+
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"