diff --git a/package.json b/package.json
index bafcd68..29d7313 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,8 @@
"react-scripts": "5.0.1",
"redux": "^4.2.1",
"redux-logger": "^3.0.6",
+ "redux-persist": "^6.0.0",
+ "redux-thunk": "^2.4.2",
"reselect": "^4.1.7",
"sass": "^1.55.0",
"styled-components": "^5.3.6",
diff --git a/src/components/spinner/spinner.component.jsx b/src/components/spinner/spinner.component.jsx
new file mode 100644
index 0000000..8557a97
--- /dev/null
+++ b/src/components/spinner/spinner.component.jsx
@@ -0,0 +1,8 @@
+import { SpinnerContainer, SpinnerOverlay } from "./spinner.styles";
+
+const Spinner = () =>
+
+
+
+
+export default Spinner;
\ No newline at end of file
diff --git a/src/components/spinner/spinner.styles.jsx b/src/components/spinner/spinner.styles.jsx
new file mode 100644
index 0000000..7650f84
--- /dev/null
+++ b/src/components/spinner/spinner.styles.jsx
@@ -0,0 +1,30 @@
+import styled from "styled-components";
+
+export const SpinnerOverlay = styled.div`
+ height: 60vh;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
+
+export const SpinnerContainer = styled.div`
+ display: inline-block;
+ width: 50px;
+ height: 50px;
+ border: 3px solid rgba(195, 195, 195, 0.6);
+ border-radius: 50%;
+ border-top-color: #636767;
+ animation: spin 1s ease-in-out infinite;
+ -webkit-animation: spin 1s ease-in-out infinite;
+ @keyframes spin {
+ to {
+ -webkit-transform: rotate(360deg);
+ }
+ }
+ @-webkit-keyframes spin {
+ to {
+ -webkit-transform: rotate(360deg);
+ }
+ }
+`;
diff --git a/src/index.js b/src/index.js
index a371921..2540555 100644
--- a/src/index.js
+++ b/src/index.js
@@ -5,15 +5,18 @@ import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import './index.css';
import App from './App';
-import { store } from './store/store';
+import { store, persistor } from './store/store';
+import { PersistGate } from 'redux-persist/integration/react';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
-
-
-
+
+
+
+
+
);
diff --git a/src/routes/categories-preview/categories-preview.component.jsx b/src/routes/categories-preview/categories-preview.component.jsx
index 41191d7..fba9f12 100644
--- a/src/routes/categories-preview/categories-preview.component.jsx
+++ b/src/routes/categories-preview/categories-preview.component.jsx
@@ -1,18 +1,22 @@
import { Fragment } from "react";
import { useSelector } from "react-redux";
-import { categoriesMapSelector } from "../../store/category/category.selector";
+import { categoriesMapSelector, selectCategoriesIsLoading } from "../../store/category/category.selector";
import CategoryPreview from "../../components/category-preview/category-preview.component";
+import Spinner from "../../components/spinner/spinner.component";
const CategoriesPreview = () => {
const categoriesMap = useSelector(categoriesMapSelector);
+ const isLoading = useSelector(selectCategoriesIsLoading);
return (
+
{
- Object.keys(categoriesMap).map(title => {
+ isLoading ? () :
+ (Object.keys(categoriesMap).map(title => {
const products = categoriesMap[title];
return
- })
+ }))
}
diff --git a/src/routes/category/category-component.jsx b/src/routes/category/category-component.jsx
index b63b91d..5f3b261 100644
--- a/src/routes/category/category-component.jsx
+++ b/src/routes/category/category-component.jsx
@@ -1,12 +1,14 @@
import { Fragment, useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useSelector } from "react-redux";
-import { categoriesMapSelector } from "../../store/category/category.selector";
+import { categoriesMapSelector, selectCategoriesIsLoading } from "../../store/category/category.selector";
import ProductCard from '../../components/product-card/product-card.component';
import './category.styles.scss';
+import Spinner from '../../components/spinner/spinner.component';
const Category = () => {
const { category } = useParams()
+ const isLoading = useSelector(selectCategoriesIsLoading);
const categoriesMap = useSelector(categoriesMapSelector);
const [products, setProducts] = useState(categoriesMap[category])
useEffect(() => {
@@ -18,11 +20,14 @@ const Category = () => {
{category}
-
- {products &&
- products.map((product) =>
)
- }
-
+ {
+ isLoading ? () : (
+ {products &&
+ products.map((product) =>
)
+ }
+
)
+ }
+
)
diff --git a/src/routes/shop/shop.component.jsx b/src/routes/shop/shop.component.jsx
index dbcb2ed..c67483c 100644
--- a/src/routes/shop/shop.component.jsx
+++ b/src/routes/shop/shop.component.jsx
@@ -1,8 +1,7 @@
import { useEffect } from 'react';
import { Routes, Route } from 'react-router-dom';
import { useDispatch } from 'react-redux';
-import { GetCategoriesAndDocuments } from '../../utils/firebase/firebase.utils';
-import { setCategories } from '../../store/category/category.action';
+import { fetchCategoriesAsync } from '../../store/category/category.action';
import CategoriesPreview from '../categories-preview/categories-preview.component';
import Category from '../category/category-component';
import './shop.styles.scss';
@@ -12,8 +11,7 @@ const Shop = () => {
useEffect(() => {
const getCategoriesMap = async () => {
- const categoriesArray = await GetCategoriesAndDocuments();
- dispatch(setCategories(categoriesArray))
+ dispatch(fetchCategoriesAsync())
}
getCategoriesMap();
diff --git a/src/store/category/category.action.js b/src/store/category/category.action.js
index c49fe3f..5575f8e 100644
--- a/src/store/category/category.action.js
+++ b/src/store/category/category.action.js
@@ -1,5 +1,21 @@
import { createAction } from "../../utils/reducer/reducer.utils"
import { CATEGORIES_ACTION_TYPE } from "./category.types"
+import { GetCategoriesAndDocuments } from '../../utils/firebase/firebase.utils';
+export const setCategories = (categories) => createAction(CATEGORIES_ACTION_TYPE.SET_CATEGORIES, categories)
-export const setCategories = (categories) => createAction(CATEGORIES_ACTION_TYPE.SET_CATEGORIES, categories)
\ No newline at end of file
+export const fetchCategoriesStart = () => createAction(CATEGORIES_ACTION_TYPE.FETCH_CATEGORIES_START)
+export const fetchCategoriesSuccess = (categoriesArray) => createAction(CATEGORIES_ACTION_TYPE.FETCH_CATEGORIES_SUCCESS, categoriesArray)
+export const fetchCategoriesFailed = (error) => createAction(CATEGORIES_ACTION_TYPE.FETCH_CATEGORIES_FAILED, error)
+
+
+export const fetchCategoriesAsync = () => async (dispatch) => {
+ dispatch(fetchCategoriesStart())
+ try {
+ const categoriesArray = await GetCategoriesAndDocuments();
+ dispatch(fetchCategoriesSuccess(categoriesArray));
+ } catch (error) {
+ dispatch(fetchCategoriesFailed(error));
+ }
+
+}
\ No newline at end of file
diff --git a/src/store/category/category.reducer.js b/src/store/category/category.reducer.js
index c32ce9f..f41969e 100644
--- a/src/store/category/category.reducer.js
+++ b/src/store/category/category.reducer.js
@@ -1,15 +1,21 @@
import { CATEGORIES_ACTION_TYPE } from "./category.types";
const CATEGORIES_INITIAL_STATE = {
- categories: []
+ categories: [],
+ isLoading: false,
+ error: null
}
export const categoryReducer = (state = CATEGORIES_INITIAL_STATE, action = {}) => {
const { type, payload } = action
switch (type) {
- case CATEGORIES_ACTION_TYPE.SET_CATEGORIES:
- return { ...state, categories: payload }
+ case CATEGORIES_ACTION_TYPE.FETCH_CATEGORIES_START:
+ return { ...state, isLoading: true }
+ case CATEGORIES_ACTION_TYPE.FETCH_CATEGORIES_SUCCESS:
+ return { ...state, categories: payload, isLoading: false }
+ case CATEGORIES_ACTION_TYPE.FETCH_CATEGORIES_FAILED:
+ return { ...state, error: payload, isLoading: false }
default:
return state;
}
diff --git a/src/store/category/category.selector.js b/src/store/category/category.selector.js
index 83924f1..9365bb0 100644
--- a/src/store/category/category.selector.js
+++ b/src/store/category/category.selector.js
@@ -16,4 +16,9 @@ export const categoriesMapSelector = createSelector(
acc[title.toLowerCase()] = items;
return acc;
}, {})
+)
+
+export const selectCategoriesIsLoading = createSelector(
+ [selectCategoryReducer],
+ (categoriesSlice) => categoriesSlice.isLoading
)
\ No newline at end of file
diff --git a/src/store/category/category.types.js b/src/store/category/category.types.js
index 0e6c579..d0490ac 100644
--- a/src/store/category/category.types.js
+++ b/src/store/category/category.types.js
@@ -1,3 +1,6 @@
export const CATEGORIES_ACTION_TYPE = {
- 'SET_CATEGORIES': 'category/SET_CATEGORIES'
+ 'FETCH_CATEGORIES_START': 'category/FETCH_CATEGORIES_START',
+ 'FETCH_CATEGORIES_SUCCESS': 'category/FETCH_CATEGORIES_SUCCESS',
+ 'FETCH_CATEGORIES_FAILED': 'category/FETCH_CATEGORIES_FAILED'
+
}
diff --git a/src/store/store.js b/src/store/store.js
index 5998b6b..734fef1 100644
--- a/src/store/store.js
+++ b/src/store/store.js
@@ -1,26 +1,22 @@
import { compose, createStore, applyMiddleware } from "redux";
+import { persistStore, persistReducer } from "redux-persist";
import logger from "redux-logger";
import { rootReducer } from "./root-reducer";
+import storage from "redux-persist/lib/storage";
+import thunk from "redux-thunk";
-
-const loggerMiddleware = (store) => (next) => (action) => {
- console.log(store)
-
- if (!action.type) {
- return next(action);
- }
-
- console.log('type', action.type)
- console.log('payload', action.payload)
- console.log('currentState', store.getState())
-
- next(action);
- console.log('nextState', store.getState())
-
+const persistConfig = {
+ key: 'root',
+ storage,
+ whitelist: ['cart']
}
-const middleWares = [logger]
+const persistedReducer = persistReducer(persistConfig, rootReducer);
+const middleWares = [process.env.NODE_ENV === 'development' && logger, thunk].filter(Boolean)
+
+const composeEnhancer = (process.env.NODE_ENV !== 'production' && window && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose;
+const composeEnhancers = composeEnhancer(applyMiddleware(...middleWares))
-const composeEnhancers = compose(applyMiddleware(...middleWares))
+export const store = createStore(persistedReducer, undefined, composeEnhancers)
-export const store = createStore(rootReducer, undefined, composeEnhancers)
\ No newline at end of file
+export const persistor = persistStore(store)
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index aaf4d5b..bec867c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8186,6 +8186,16 @@ redux-logger@^3.0.6:
dependencies:
deep-diff "^0.3.5"
+redux-persist@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8"
+ integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==
+
+redux-thunk@^2.4.2:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b"
+ integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==
+
redux@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"