├── .gitignore
├── README.md
├── package.json
├── public
├── africa.png
├── images
│ ├── africa.png
│ ├── arachide.png
│ ├── bg.jpg
│ ├── bg.png
│ ├── bgr.png
│ ├── bobolos.png
│ ├── desktop.ini
│ ├── diversity.png
│ ├── divine.png
│ ├── looking_shop.png
│ ├── manioc.png
│ ├── max.png
│ ├── nkui.png
│ ├── ohg.png
│ ├── okok.png
│ ├── palmoil.png
│ ├── quatrecote.png
│ ├── rondelle.png
│ ├── s1.png
│ ├── s10.png
│ ├── s11.png
│ ├── s12.png
│ ├── s13.png
│ ├── s14.png
│ ├── s15.png
│ ├── s16.png
│ ├── s17.png
│ ├── s18.png
│ ├── s19.png
│ ├── s2.png
│ ├── s20.png
│ ├── s21.png
│ ├── s3.png
│ ├── s4.png
│ ├── s5.jpg
│ ├── s6.png
│ ├── s7.png
│ ├── s8.png
│ ├── s9.png
│ ├── shop.png
│ ├── shop_open.png
│ └── yam.png
├── index.html
├── manifest.json
└── robots.txt
└── src
├── App.js
├── actions
├── cartActions.js
├── orderActions.js
├── productActions.js
├── shopActions.js
└── userActions.js
├── bootstrap.min.css
├── components
├── CheckoutSteps.js
├── Footer.js
├── FormContainer.js
├── Header.js
├── Loader.js
├── Message.js
├── Paginate.js
├── PaginateShop.js
├── Product.js
├── ProductCarousel.js
├── Rating.js
├── SearchProduct.js
├── SearchShop.js
├── Shop.js
└── ShopCarousel.js
├── constants
├── cartConstants.js
├── orderConstants.js
├── productConstants.js
├── shopConstants.js
└── userConstants.js
├── index.css
├── index.js
├── products.js
├── reducers
├── cartReducers.js
├── orderReducers.js
├── productReducers.js
├── shopReducers.js
└── userReducers.js
├── reportWebVitals.js
├── screens
├── CartScreen.js
├── GrowBusinessScreen.js
├── HomeProductScreen.js
├── HomeScreen.js
├── IndexSearchScreen.js
├── Index_Screen.js
├── LoginScreen.js
├── OpenYourStoreScreen.js
├── OrderListScreen.js
├── OrderScreen.js
├── PaymentScreen.js
├── PlaceOrderScreen.js
├── ProductEditScreen.js
├── ProductListScreen.js
├── ProductScreen.js
├── ProfileScreen.js
├── RegisterScreen.js
├── ResultProductScreen.js
├── ResultShopScreen.js
├── ShippingScreen.js
├── ShopScreen.js
├── UserEditScreen.js
└── UserListScreen.js
├── shops.js
└── store.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React eCommerce Frontend
2 |
3 | In the project directory, you can run:
4 |
5 | ### `npm install`
6 | ### `npm start`
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "proxy": "http://127.0.0.1:8000",
4 | "version": "0.1.0",
5 | "private": true,
6 | "dependencies": {
7 | "@testing-library/jest-dom": "^5.11.9",
8 | "@testing-library/react": "^11.2.5",
9 | "@testing-library/user-event": "^12.8.3",
10 | "axios": "^0.21.1",
11 | "moment": "^2.29.1",
12 | "react": "^17.0.2",
13 | "react-bootstrap": "^1.5.2",
14 | "react-dom": "^17.0.2",
15 | "react-native": "^0.64.0",
16 | "react-native-web": "^0.15.0",
17 | "react-paypal-button-v2": "^2.6.3",
18 | "react-redux": "^7.2.3",
19 | "react-router-bootstrap": "^0.25.0",
20 | "react-router-dom": "^5.2.0",
21 | "react-scripts": "4.0.3",
22 | "redux": "^4.0.5",
23 | "redux-devtools-extension": "^2.13.9",
24 | "redux-thunk": "^2.3.0",
25 | "web-vitals": "^1.1.1"
26 | },
27 | "scripts": {
28 | "start": "react-scripts start",
29 | "build": "react-scripts build",
30 | "test": "react-scripts test",
31 | "eject": "react-scripts eject"
32 | },
33 | "eslintConfig": {
34 | "extends": [
35 | "react-app",
36 | "react-app/jest"
37 | ]
38 | },
39 | "browserslist": {
40 | "production": [
41 | ">0.2%",
42 | "not dead",
43 | "not op_mini all"
44 | ],
45 | "development": [
46 | "last 1 chrome version",
47 | "last 1 firefox version",
48 | "last 1 safari version"
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/public/africa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/africa.png
--------------------------------------------------------------------------------
/public/images/africa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/africa.png
--------------------------------------------------------------------------------
/public/images/arachide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/arachide.png
--------------------------------------------------------------------------------
/public/images/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/bg.jpg
--------------------------------------------------------------------------------
/public/images/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/bg.png
--------------------------------------------------------------------------------
/public/images/bgr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/bgr.png
--------------------------------------------------------------------------------
/public/images/bobolos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/bobolos.png
--------------------------------------------------------------------------------
/public/images/desktop.ini:
--------------------------------------------------------------------------------
1 | [.ShellClassInfo]
2 | LocalizedResourceName=image
3 |
--------------------------------------------------------------------------------
/public/images/diversity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/diversity.png
--------------------------------------------------------------------------------
/public/images/divine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/divine.png
--------------------------------------------------------------------------------
/public/images/looking_shop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/looking_shop.png
--------------------------------------------------------------------------------
/public/images/manioc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/manioc.png
--------------------------------------------------------------------------------
/public/images/max.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/max.png
--------------------------------------------------------------------------------
/public/images/nkui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/nkui.png
--------------------------------------------------------------------------------
/public/images/ohg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/ohg.png
--------------------------------------------------------------------------------
/public/images/okok.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/okok.png
--------------------------------------------------------------------------------
/public/images/palmoil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/palmoil.png
--------------------------------------------------------------------------------
/public/images/quatrecote.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/quatrecote.png
--------------------------------------------------------------------------------
/public/images/rondelle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/rondelle.png
--------------------------------------------------------------------------------
/public/images/s1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s1.png
--------------------------------------------------------------------------------
/public/images/s10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s10.png
--------------------------------------------------------------------------------
/public/images/s11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s11.png
--------------------------------------------------------------------------------
/public/images/s12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s12.png
--------------------------------------------------------------------------------
/public/images/s13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s13.png
--------------------------------------------------------------------------------
/public/images/s14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s14.png
--------------------------------------------------------------------------------
/public/images/s15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s15.png
--------------------------------------------------------------------------------
/public/images/s16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s16.png
--------------------------------------------------------------------------------
/public/images/s17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s17.png
--------------------------------------------------------------------------------
/public/images/s18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s18.png
--------------------------------------------------------------------------------
/public/images/s19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s19.png
--------------------------------------------------------------------------------
/public/images/s2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s2.png
--------------------------------------------------------------------------------
/public/images/s20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s20.png
--------------------------------------------------------------------------------
/public/images/s21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s21.png
--------------------------------------------------------------------------------
/public/images/s3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s3.png
--------------------------------------------------------------------------------
/public/images/s4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s4.png
--------------------------------------------------------------------------------
/public/images/s5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s5.jpg
--------------------------------------------------------------------------------
/public/images/s6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s6.png
--------------------------------------------------------------------------------
/public/images/s7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s7.png
--------------------------------------------------------------------------------
/public/images/s8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s8.png
--------------------------------------------------------------------------------
/public/images/s9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/s9.png
--------------------------------------------------------------------------------
/public/images/shop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/shop.png
--------------------------------------------------------------------------------
/public/images/shop_open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/shop_open.png
--------------------------------------------------------------------------------
/public/images/yam.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fhrryDeveloper/React-Django-Shope-frontend/cc52d1bade5ccac7f9b3e11e062fba74e309f9b8/public/images/yam.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
16 |
17 |
18 |
27 | AfroShopMe
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { Container } from 'react-bootstrap'
2 | import { BrowserRouter as Router, Route} from 'react-router-dom'
3 | import Header from './components/Header'
4 | import Footer from './components/Footer'
5 | import HomeScreen from './screens/HomeScreen'
6 | import Index_Screen from './screens/Index_Screen'
7 | import ShopScreen from './screens/ShopScreen'
8 | import HomeProductScreen from './screens/HomeProductScreen'
9 | import ProductScreen from './screens/ProductScreen'
10 | import CartScreen from './screens/CartScreen'
11 | import LoginScreen from './screens/LoginScreen'
12 | import RegisterScreen from './screens/RegisterScreen'
13 | import ProfileScreen from './screens/ProfileScreen'
14 | import ShippingScreen from './screens/ShippingScreen'
15 | import PaymentScreen from './screens/PaymentScreen'
16 | import PlaceOrderScreen from './screens/PlaceOrderScreen'
17 | import OrderScreen from './screens/OrderScreen'
18 | import UserListScreen from './screens/UserListScreen'
19 | import UserEditScreen from './screens/UserEditScreen'
20 | import ProductListScreen from './screens/ProductListScreen'
21 | import ProductEditScreen from './screens/ProductEditScreen'
22 | import OrderListScreen from './screens/OrderListScreen'
23 | import IndexSearchScreen from './screens/IndexSearchScreen'
24 | import OpenYourStoreScreen from './screens/OpenYourStoreScreen'
25 | import GrowBusinessScreen from './screens/GrowBusinessScreen'
26 | import ResultProductScreen from './screens/ResultProductScreen'
27 | import ResultShopScreen from './screens/ResultShopScreen'
28 |
29 |
30 | function App() {
31 | return (
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | );
72 | }
73 |
74 | export default App;
75 |
--------------------------------------------------------------------------------
/src/actions/cartActions.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import {
3 | CART_ADD_ITEM,
4 | CART_REMOVE_ITEM,
5 | CART_SAVE_SHIPPING_ADDRESS,
6 |
7 | CART_SAVE_PAYMENT_METHOD,
8 | } from '../constants/cartConstants'
9 |
10 |
11 | export const addToCart = (id, qty) => async (dispatch, getState) => {
12 | const { data } = await axios.get(`/api/products/${id}`)
13 |
14 | dispatch({
15 | type: CART_ADD_ITEM,
16 | payload: {
17 | product: data._id,
18 | name: data.name,
19 | image: data.image,
20 | price: data.price,
21 | countInStock: data.countInStock,
22 | qty
23 | }
24 | })
25 | localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems))
26 | }
27 |
28 |
29 |
30 | export const removeFromCart = (id) => (dispatch, getState) => {
31 | dispatch({
32 | type: CART_REMOVE_ITEM,
33 | payload: id,
34 | })
35 |
36 | localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems))
37 | }
38 |
39 |
40 | export const saveShippingAddress = (data) => (dispatch) => {
41 | dispatch({
42 | type: CART_SAVE_SHIPPING_ADDRESS,
43 | payload: data,
44 | })
45 |
46 | localStorage.setItem('shippingAddress', JSON.stringify(data))
47 | }
48 |
49 |
50 | export const savePaymentMethod = (data) => (dispatch) => {
51 | dispatch({
52 | type: CART_SAVE_PAYMENT_METHOD,
53 | payload: data,
54 | })
55 |
56 | localStorage.setItem('paymentMethod', JSON.stringify(data))
57 | }
--------------------------------------------------------------------------------
/src/actions/orderActions.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import {
3 | ORDER_CREATE_REQUEST,
4 | ORDER_CREATE_SUCCESS,
5 | ORDER_CREATE_FAIL,
6 |
7 | ORDER_DETAILS_REQUEST,
8 | ORDER_DETAILS_SUCCESS,
9 | ORDER_DETAILS_FAIL,
10 |
11 | ORDER_PAY_REQUEST,
12 | ORDER_PAY_SUCCESS,
13 | ORDER_PAY_FAIL,
14 | //ORDER_PAY_RESET,
15 |
16 | ORDER_LIST_MY_REQUEST,
17 | ORDER_LIST_MY_SUCCESS,
18 | ORDER_LIST_MY_FAIL,
19 | // ORDER_LIST_MY_RESET,
20 |
21 | ORDER_LIST_REQUEST,
22 | ORDER_LIST_SUCCESS,
23 | ORDER_LIST_FAIL,
24 |
25 | ORDER_DELIVER_REQUEST,
26 | ORDER_DELIVER_SUCCESS,
27 | ORDER_DELIVER_FAIL,
28 | //ORDER_DELIVER_RESET,
29 |
30 | } from '../constants/orderConstants'
31 |
32 |
33 | import { CART_CLEAR_ITEMS } from '../constants/cartConstants'
34 |
35 |
36 | export const createOrder = (order) => async (dispatch, getState) => {
37 | try {
38 | dispatch({
39 | type: ORDER_CREATE_REQUEST
40 | })
41 |
42 | const {
43 | userLogin: { userInfo },
44 | } = getState()
45 |
46 | const config = {
47 | headers: {
48 | 'Content-type': 'application/json',
49 | Authorization: `Bearer ${userInfo.token}`
50 | }
51 | }
52 |
53 | const { data } = await axios.post(
54 | `/api/orders/add/`,
55 | order,
56 | config
57 | )
58 |
59 | dispatch({
60 | type: ORDER_CREATE_SUCCESS,
61 | payload: data
62 | })
63 |
64 | dispatch({
65 | type: CART_CLEAR_ITEMS,
66 | payload: data
67 | })
68 |
69 | localStorage.removeItem('cartItems')
70 |
71 |
72 | } catch (error) {
73 | dispatch({
74 | type: ORDER_CREATE_FAIL,
75 | payload: error.response && error.response.data.detail
76 | ? error.response.data.detail
77 | : error.message,
78 | })
79 | }
80 | }
81 |
82 |
83 | export const getOrderDetails = (id) => async (dispatch, getState) => {
84 | try {
85 | dispatch({
86 | type: ORDER_DETAILS_REQUEST
87 | })
88 |
89 | const {
90 | userLogin: { userInfo },
91 | } = getState()
92 |
93 | const config = {
94 | headers: {
95 | 'Content-type': 'application/json',
96 | Authorization: `Bearer ${userInfo.token}`
97 | }
98 | }
99 |
100 | const { data } = await axios.get(
101 | `/api/orders/${id}/`,
102 | config
103 | )
104 |
105 | dispatch({
106 | type: ORDER_DETAILS_SUCCESS,
107 | payload: data
108 | })
109 |
110 |
111 | } catch (error) {
112 | dispatch({
113 | type: ORDER_DETAILS_FAIL,
114 | payload: error.response && error.response.data.detail
115 | ? error.response.data.detail
116 | : error.message,
117 | })
118 | }
119 | }
120 |
121 |
122 | export const payOrder = (id, paymentResult) => async (dispatch, getState) => {
123 | try {
124 | dispatch({
125 | type: ORDER_PAY_REQUEST
126 | })
127 |
128 | const {
129 | userLogin: { userInfo },
130 | } = getState()
131 |
132 | const config = {
133 | headers: {
134 | 'Content-type': 'application/json',
135 | Authorization: `Bearer ${userInfo.token}`
136 | }
137 | }
138 |
139 | const { data } = await axios.put(
140 | `/api/orders/${id}/pay/`,
141 | paymentResult,
142 | config
143 | )
144 |
145 | dispatch({
146 | type: ORDER_PAY_SUCCESS,
147 | payload: data
148 | })
149 |
150 |
151 | } catch (error) {
152 | dispatch({
153 | type: ORDER_PAY_FAIL,
154 | payload: error.response && error.response.data.detail
155 | ? error.response.data.detail
156 | : error.message,
157 | })
158 | }
159 | }
160 |
161 |
162 | export const deliverOrder = (order) => async (dispatch, getState) => {
163 | try {
164 | dispatch({
165 | type: ORDER_DELIVER_REQUEST
166 | })
167 |
168 | const {
169 | userLogin: { userInfo },
170 | } = getState()
171 |
172 | const config = {
173 | headers: {
174 | 'Content-type': 'application/json',
175 | Authorization: `Bearer ${userInfo.token}`
176 | }
177 | }
178 |
179 | const { data } = await axios.put(
180 | `/api/orders/${order._id}/deliver/`,
181 | {},
182 | config
183 | )
184 |
185 | dispatch({
186 | type: ORDER_DELIVER_SUCCESS,
187 | payload: data
188 | })
189 |
190 |
191 | } catch (error) {
192 | dispatch({
193 | type: ORDER_DELIVER_FAIL,
194 | payload: error.response && error.response.data.detail
195 | ? error.response.data.detail
196 | : error.message,
197 | })
198 | }
199 | }
200 |
201 |
202 | export const listMyOrders = () => async (dispatch, getState) => {
203 | try {
204 | dispatch({
205 | type: ORDER_LIST_MY_REQUEST
206 | })
207 |
208 | const {
209 | userLogin: { userInfo },
210 | } = getState()
211 |
212 | const config = {
213 | headers: {
214 | 'Content-type': 'application/json',
215 | Authorization: `Bearer ${userInfo.token}`
216 | }
217 | }
218 |
219 | const { data } = await axios.get(
220 | `/api/orders/myorders/`,
221 | config
222 | )
223 |
224 | dispatch({
225 | type: ORDER_LIST_MY_SUCCESS,
226 | payload: data
227 | })
228 |
229 |
230 | } catch (error) {
231 | dispatch({
232 | type: ORDER_LIST_MY_FAIL,
233 | payload: error.response && error.response.data.detail
234 | ? error.response.data.detail
235 | : error.message,
236 | })
237 | }
238 | }
239 |
240 | export const listOrders = () => async (dispatch, getState) => {
241 | try {
242 | dispatch({
243 | type: ORDER_LIST_REQUEST
244 | })
245 |
246 | const {
247 | userLogin: { userInfo },
248 | } = getState()
249 |
250 | const config = {
251 | headers: {
252 | 'Content-type': 'application/json',
253 | Authorization: `Bearer ${userInfo.token}`
254 | }
255 | }
256 |
257 | const { data } = await axios.get(
258 | `/api/orders/`,
259 | config
260 | )
261 |
262 | dispatch({
263 | type: ORDER_LIST_SUCCESS,
264 | payload: data
265 | })
266 |
267 |
268 | } catch (error) {
269 | dispatch({
270 | type: ORDER_LIST_FAIL,
271 | payload: error.response && error.response.data.detail
272 | ? error.response.data.detail
273 | : error.message,
274 | })
275 | }
276 | }
--------------------------------------------------------------------------------
/src/actions/productActions.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import {
3 | PRODUCT_LIST_REQUEST,
4 | PRODUCT_LIST_SUCCESS,
5 | PRODUCT_LIST_FAIL,
6 |
7 | PRODUCT_DETAILS_REQUEST,
8 | PRODUCT_DETAILS_SUCCESS,
9 | PRODUCT_DETAILS_FAIL,
10 |
11 | PRODUCT_DELETE_REQUEST,
12 | PRODUCT_DELETE_SUCCESS,
13 | PRODUCT_DELETE_FAIL,
14 |
15 | PRODUCT_CREATE_REQUEST,
16 | PRODUCT_CREATE_SUCCESS,
17 | PRODUCT_CREATE_FAIL,
18 |
19 | PRODUCT_UPDATE_REQUEST,
20 | PRODUCT_UPDATE_SUCCESS,
21 | PRODUCT_UPDATE_FAIL,
22 |
23 | PRODUCT_CREATE_REVIEW_REQUEST,
24 | PRODUCT_CREATE_REVIEW_SUCCESS,
25 | PRODUCT_CREATE_REVIEW_FAIL,
26 |
27 | PRODUCT_TOP_REQUEST,
28 | PRODUCT_TOP_SUCCESS,
29 | PRODUCT_TOP_FAIL,
30 | } from '../constants/productConstants'
31 |
32 | export const listProducts = (keyword = '') => async (dispatch) => {
33 | try {
34 | dispatch({ type: PRODUCT_LIST_REQUEST })
35 |
36 | const { data } = await axios.get(`/api/products${keyword}`)
37 |
38 | dispatch({
39 | type: PRODUCT_LIST_SUCCESS,
40 | payload: data
41 | })
42 |
43 | } catch (error) {
44 | dispatch({
45 | type: PRODUCT_LIST_FAIL,
46 | payload: error.response && error.response.data.detail
47 | ? error.response.data.detail
48 | : error.message,
49 | })
50 | }
51 | }
52 |
53 | export const listProductDetails = (id) => async (dispatch) => {
54 | try {
55 | dispatch({ type: PRODUCT_DETAILS_REQUEST })
56 |
57 | const { data } = await axios.get(`/api/products/${id}`)
58 |
59 | dispatch({
60 | type: PRODUCT_DETAILS_SUCCESS,
61 | payload: data
62 | })
63 |
64 | } catch (error) {
65 | dispatch({
66 | type: PRODUCT_DETAILS_FAIL,
67 | payload: error.response && error.response.data.detail
68 | ? error.response.data.detail
69 | : error.message,
70 | })
71 | }
72 | }
73 |
74 |
75 | export const deleteProduct = (id) => async (dispatch, getState) => {
76 | try {
77 | dispatch({
78 | type: PRODUCT_DELETE_REQUEST
79 | })
80 |
81 | const {
82 | userLogin: { userInfo },
83 | } = getState()
84 |
85 | const config = {
86 | headers: {
87 | 'Content-type': 'application/json',
88 | Authorization: `Bearer ${userInfo.token}`
89 | }
90 | }
91 |
92 | const { data } = await axios.delete(
93 | `/api/products/delete/${id}/`,
94 | config
95 | )
96 |
97 | dispatch({
98 | type: PRODUCT_DELETE_SUCCESS,
99 | })
100 |
101 |
102 | } catch (error) {
103 | dispatch({
104 | type: PRODUCT_DELETE_FAIL,
105 | payload: error.response && error.response.data.detail
106 | ? error.response.data.detail
107 | : error.message,
108 | })
109 | }
110 | }
111 |
112 |
113 |
114 |
115 | export const createProduct = () => async (dispatch, getState) => {
116 | try {
117 | dispatch({
118 | type: PRODUCT_CREATE_REQUEST
119 | })
120 |
121 | const {
122 | userLogin: { userInfo },
123 | } = getState()
124 |
125 | const config = {
126 | headers: {
127 | 'Content-type': 'application/json',
128 | Authorization: `Bearer ${userInfo.token}`
129 | }
130 | }
131 |
132 | const { data } = await axios.post(
133 | `/api/products/create/`,
134 | {},
135 | config
136 | )
137 | dispatch({
138 | type: PRODUCT_CREATE_SUCCESS,
139 | payload: data,
140 | })
141 |
142 |
143 | } catch (error) {
144 | dispatch({
145 | type: PRODUCT_CREATE_FAIL,
146 | payload: error.response && error.response.data.detail
147 | ? error.response.data.detail
148 | : error.message,
149 | })
150 | }
151 | }
152 |
153 |
154 |
155 | export const updateProduct = (product) => async (dispatch, getState) => {
156 | try {
157 | dispatch({
158 | type: PRODUCT_UPDATE_REQUEST
159 | })
160 |
161 | const {
162 | userLogin: { userInfo },
163 | } = getState()
164 |
165 | const config = {
166 | headers: {
167 | 'Content-type': 'application/json',
168 | Authorization: `Bearer ${userInfo.token}`
169 | }
170 | }
171 |
172 | const { data } = await axios.put(
173 | `/api/products/update/${product._id}/`,
174 | product,
175 | config
176 | )
177 | dispatch({
178 | type: PRODUCT_UPDATE_SUCCESS,
179 | payload: data,
180 | })
181 |
182 |
183 | dispatch({
184 | type: PRODUCT_DETAILS_SUCCESS,
185 | payload: data
186 | })
187 |
188 |
189 | } catch (error) {
190 | dispatch({
191 | type: PRODUCT_UPDATE_FAIL,
192 | payload: error.response && error.response.data.detail
193 | ? error.response.data.detail
194 | : error.message,
195 | })
196 | }
197 | }
198 |
199 |
200 | export const createProductReview = (productId, review) => async (dispatch, getState) => {
201 | try {
202 | dispatch({
203 | type: PRODUCT_CREATE_REVIEW_REQUEST
204 | })
205 |
206 | const {
207 | userLogin: { userInfo },
208 | } = getState()
209 |
210 | const config = {
211 | headers: {
212 | 'Content-type': 'application/json',
213 | Authorization: `Bearer ${userInfo.token}`
214 | }
215 | }
216 |
217 | const { data } = await axios.post(
218 | `/api/products/${productId}/reviews/`,
219 | review,
220 | config
221 | )
222 | dispatch({
223 | type: PRODUCT_CREATE_REVIEW_SUCCESS,
224 | payload: data,
225 | })
226 |
227 |
228 |
229 | } catch (error) {
230 | dispatch({
231 | type: PRODUCT_CREATE_REVIEW_FAIL,
232 | payload: error.response && error.response.data.detail
233 | ? error.response.data.detail
234 | : error.message,
235 | })
236 | }
237 | }
238 |
239 |
240 | export const listTopProducts = () => async (dispatch) => {
241 | try {
242 | dispatch({ type: PRODUCT_TOP_REQUEST })
243 |
244 | const { data } = await axios.get(`/api/products/top/`)
245 |
246 | dispatch({
247 | type: PRODUCT_TOP_SUCCESS,
248 | payload: data
249 | })
250 |
251 | } catch (error) {
252 | dispatch({
253 | type: PRODUCT_TOP_FAIL,
254 | payload: error.response && error.response.data.detail
255 | ? error.response.data.detail
256 | : error.message,
257 | })
258 | }
259 | }
--------------------------------------------------------------------------------
/src/actions/shopActions.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import {
3 | SHOP_LIST_REQUEST,
4 | SHOP_LIST_SUCCESS,
5 | SHOP_LIST_FAIL,
6 |
7 | SHOP_DETAILS_REQUEST,
8 | SHOP_DETAILS_SUCCESS,
9 | SHOP_DETAILS_FAIL,
10 |
11 | SHOP_CREATE_REVIEW_REQUEST,
12 | SHOP_CREATE_REVIEW_SUCCESS,
13 | SHOP_CREATE_REVIEW_FAIL,
14 |
15 | SHOP_TOP_REQUEST,
16 | SHOP_TOP_SUCCESS,
17 | SHOP_TOP_FAIL,
18 | } from '../constants/shopConstants'
19 |
20 | export const listShops = (keyword = '') => async (dispatch) => {
21 | try {
22 | dispatch({ type: SHOP_LIST_REQUEST })
23 |
24 | const { data } = await axios.get(`/api/shops${keyword}`)
25 |
26 | dispatch({
27 | type: SHOP_LIST_SUCCESS,
28 | payload: data
29 | })
30 |
31 | } catch (error) {
32 | dispatch({
33 | type: SHOP_LIST_FAIL,
34 | payload: error.response && error.response.data.message
35 | ? error.response.data.message
36 | : error.message,
37 | })
38 | }
39 | }
40 |
41 | export const listShopDetails = (id) => async (dispatch) => {
42 | try {
43 | dispatch({ type: SHOP_DETAILS_REQUEST })
44 |
45 | const { data } = await axios.get(`/api/shops/${id}`)
46 |
47 | dispatch({
48 | type: SHOP_DETAILS_SUCCESS,
49 | payload: data
50 | })
51 |
52 | } catch (error) {
53 | dispatch({
54 | type: SHOP_DETAILS_FAIL,
55 | payload: error.response && error.response.data.message
56 | ? error.response.data.message
57 | : error.message,
58 | })
59 | }
60 | }
61 |
62 |
63 | export const createShopReview = (shopId, review) => async (dispatch, getState) => {
64 | try {
65 | dispatch({
66 | type: SHOP_CREATE_REVIEW_REQUEST
67 | })
68 |
69 | const {
70 | userLogin: { userInfo },
71 | } = getState()
72 |
73 | const config = {
74 | headers: {
75 | 'Content-type': 'application/json',
76 | Authorization: `Bearer ${userInfo.token}`
77 | }
78 | }
79 |
80 | const { data } = await axios.post(
81 | `/api/shops/${shopId}/reviews_shop/`,
82 | review,
83 | config
84 | )
85 | dispatch({
86 | type: SHOP_CREATE_REVIEW_SUCCESS,
87 | payload: data,
88 | })
89 |
90 |
91 |
92 | } catch (error) {
93 | dispatch({
94 | type: SHOP_CREATE_REVIEW_FAIL,
95 | payload: error.response && error.response.data.detail
96 | ? error.response.data.detail
97 | : error.message,
98 | })
99 | }
100 | }
101 |
102 |
103 | export const listTopShops = () => async (dispatch) => {
104 | try {
105 | dispatch({ type: SHOP_TOP_REQUEST })
106 |
107 | const { data } = await axios.get(`/api/shops/top_shop/`)
108 |
109 | dispatch({
110 | type: SHOP_TOP_SUCCESS,
111 | payload: data
112 | })
113 |
114 | } catch (error) {
115 | dispatch({
116 | type: SHOP_TOP_FAIL,
117 | payload: error.response && error.response.data.detail
118 | ? error.response.data.detail
119 | : error.message,
120 | })
121 | }
122 | }
--------------------------------------------------------------------------------
/src/actions/userActions.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import {
3 | USER_LOGIN_REQUEST,
4 | USER_LOGIN_SUCCESS,
5 | USER_LOGIN_FAIL,
6 |
7 | USER_LOGOUT,
8 |
9 | USER_REGISTER_REQUEST,
10 | USER_REGISTER_SUCCESS,
11 | USER_REGISTER_FAIL,
12 |
13 | USER_DETAILS_REQUEST,
14 | USER_DETAILS_SUCCESS,
15 | USER_DETAILS_FAIL,
16 | USER_DETAILS_RESET,
17 |
18 | USER_UPDATE_PROFILE_REQUEST,
19 | USER_UPDATE_PROFILE_SUCCESS,
20 | USER_UPDATE_PROFILE_FAIL,
21 | //USER_UPDATE_PROFILE_RESET,
22 |
23 | USER_LIST_REQUEST,
24 | USER_LIST_SUCCESS,
25 | USER_LIST_FAIL,
26 | // USER_LIST_RESET,
27 |
28 | USER_DELETE_REQUEST,
29 | USER_DELETE_SUCCESS,
30 | USER_DELETE_FAIL,
31 |
32 | USER_UPDATE_REQUEST,
33 | USER_UPDATE_SUCCESS,
34 | USER_UPDATE_FAIL,
35 | } from '../constants/userConstants'
36 |
37 | import { ORDER_LIST_MY_RESET } from '../constants/orderConstants'
38 |
39 | //login
40 | export const login = (email, password) => async (dispatch) => {
41 | try {
42 | dispatch({
43 | type: USER_LOGIN_REQUEST
44 | })
45 |
46 | const config = {
47 | headers: {
48 | 'Content-type': 'application/json'
49 | }
50 | }
51 |
52 | const { data } = await axios.post(
53 | '/api/users/login_client/',
54 | { 'username': email, 'password': password },
55 | config
56 | )
57 |
58 | dispatch({
59 | type: USER_LOGIN_SUCCESS,
60 | payload: data
61 | })
62 |
63 | localStorage.setItem('userInfo', JSON.stringify(data))
64 |
65 | } catch (error) {
66 | dispatch({
67 | type: USER_LOGIN_FAIL,
68 | payload: error.response && error.response.data.detail
69 | ? error.response.data.detail
70 | : error.message,
71 | })
72 | }
73 | }
74 |
75 |
76 | //logout
77 | export const logout = () => (dispatch) => {
78 | localStorage.removeItem('userInfo')
79 | dispatch({ type: USER_LOGOUT })
80 | dispatch({ type: USER_DETAILS_RESET })
81 | dispatch({ type: ORDER_LIST_MY_RESET })
82 |
83 | }
84 |
85 | //register
86 | export const register = (name, email, password) => async (dispatch) => {
87 | try {
88 | dispatch({
89 | type: USER_REGISTER_REQUEST
90 | })
91 |
92 | const config = {
93 | headers: {
94 | 'Content-type': 'application/json'
95 | }
96 | }
97 |
98 | const { data } = await axios.post(
99 | '/api/users/register/',
100 | { 'name': name, 'email': email, 'password': password },
101 | config
102 | )
103 |
104 | dispatch({
105 | type: USER_REGISTER_SUCCESS,
106 | payload: data
107 | })
108 |
109 | dispatch({
110 | type: USER_LOGIN_SUCCESS,
111 | payload: data
112 | })
113 |
114 | localStorage.setItem('userInfo', JSON.stringify(data))
115 |
116 | } catch (error) {
117 | dispatch({
118 | type: USER_REGISTER_FAIL,
119 | payload: error.response && error.response.data.detail
120 | ? error.response.data.detail
121 | : error.message,
122 | })
123 | }
124 | }
125 |
126 |
127 | export const getUserDetails = (id) => async (dispatch, getState) => {
128 | try {
129 | dispatch({
130 | type: USER_DETAILS_REQUEST
131 | })
132 |
133 | const {
134 | userLogin: { userInfo },
135 | } = getState()
136 |
137 | const config = {
138 | headers: {
139 | 'Content-type': 'application/json',
140 | Authorization: `Bearer ${userInfo.token}`
141 | }
142 | }
143 |
144 | const { data } = await axios.get(
145 | `/api/users/${id}/`,
146 | config
147 | )
148 |
149 | dispatch({
150 | type: USER_DETAILS_SUCCESS,
151 | payload: data
152 | })
153 |
154 |
155 | } catch (error) {
156 | dispatch({
157 | type: USER_DETAILS_FAIL,
158 | payload: error.response && error.response.data.detail
159 | ? error.response.data.detail
160 | : error.message,
161 | })
162 | }
163 | }
164 |
165 |
166 | export const updateUserProfile = (user) => async (dispatch, getState) => {
167 | try {
168 | dispatch({
169 | type: USER_UPDATE_PROFILE_REQUEST
170 | })
171 |
172 | const {
173 | userLogin: { userInfo },
174 | } = getState()
175 |
176 | const config = {
177 | headers: {
178 | 'Content-type': 'application/json',
179 | Authorization: `Bearer ${userInfo.token}`
180 | }
181 | }
182 |
183 | const { data } = await axios.put(
184 | `/api/users/profile/update/`,
185 | user,
186 | config
187 | )
188 |
189 | dispatch({
190 | type: USER_UPDATE_PROFILE_SUCCESS,
191 | payload: data
192 | })
193 |
194 | dispatch({
195 | type: USER_LOGIN_SUCCESS,
196 | payload: data
197 | })
198 |
199 | localStorage.setItem('userInfo', JSON.stringify(data))
200 |
201 | } catch (error) {
202 | dispatch({
203 | type: USER_UPDATE_PROFILE_FAIL,
204 | payload: error.response && error.response.data.detail
205 | ? error.response.data.detail
206 | : error.message,
207 | })
208 | }
209 | }
210 |
211 |
212 | export const listUsers = () => async (dispatch, getState) => {
213 | try {
214 | dispatch({
215 | type: USER_LIST_REQUEST
216 | })
217 |
218 | const {
219 | userLogin: { userInfo },
220 | } = getState()
221 |
222 | const config = {
223 | headers: {
224 | 'Content-type': 'application/json',
225 | Authorization: `Bearer ${userInfo.token}`
226 | }
227 | }
228 |
229 | const { data } = await axios.get(
230 | `/api/users/`,
231 | config
232 | )
233 |
234 | dispatch({
235 | type: USER_LIST_SUCCESS,
236 | payload: data
237 | })
238 |
239 |
240 | } catch (error) {
241 | dispatch({
242 | type: USER_LIST_FAIL,
243 | payload: error.response && error.response.data.detail
244 | ? error.response.data.detail
245 | : error.message,
246 | })
247 | }
248 | }
249 |
250 |
251 | export const deleteUser = (id) => async (dispatch, getState) => {
252 | try {
253 | dispatch({
254 | type: USER_DELETE_REQUEST
255 | })
256 |
257 | const {
258 | userLogin: { userInfo },
259 | } = getState()
260 |
261 | const config = {
262 | headers: {
263 | 'Content-type': 'application/json',
264 | Authorization: `Bearer ${userInfo.token}`
265 | }
266 | }
267 |
268 | const { data } = await axios.delete(
269 | `/api/users/delete/${id}/`,
270 | config
271 | )
272 |
273 | dispatch({
274 | type: USER_DELETE_SUCCESS,
275 | payload: data
276 | })
277 |
278 |
279 | } catch (error) {
280 | dispatch({
281 | type: USER_DELETE_FAIL,
282 | payload: error.response && error.response.data.detail
283 | ? error.response.data.detail
284 | : error.message,
285 | })
286 | }
287 | }
288 |
289 |
290 | export const updateUser = (user) => async (dispatch, getState) => {
291 | try {
292 | dispatch({
293 | type: USER_UPDATE_REQUEST
294 | })
295 |
296 | const {
297 | userLogin: { userInfo },
298 | } = getState()
299 |
300 | const config = {
301 | headers: {
302 | 'Content-type': 'application/json',
303 | Authorization: `Bearer ${userInfo.token}`
304 | }
305 | }
306 |
307 | const { data } = await axios.put(
308 | `/api/users/update/${user._id}/`,
309 | user,
310 | config
311 | )
312 |
313 | dispatch({
314 | type: USER_UPDATE_SUCCESS,
315 | })
316 |
317 | dispatch({
318 | type: USER_DETAILS_SUCCESS,
319 | payload: data
320 | })
321 |
322 |
323 | } catch (error) {
324 | dispatch({
325 | type: USER_UPDATE_FAIL,
326 | payload: error.response && error.response.data.detail
327 | ? error.response.data.detail
328 | : error.message,
329 | })
330 | }
331 | }
--------------------------------------------------------------------------------
/src/components/CheckoutSteps.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Nav } from 'react-bootstrap'
3 | import { LinkContainer } from 'react-router-bootstrap'
4 |
5 | function CheckoutSteps({ step1, step2, step3, step4 }) {
6 |
7 | return (
8 |
49 | )
50 | }
51 |
52 | export default CheckoutSteps
53 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Container, Row, Col } from 'react-bootstrap'
3 |
4 |
5 | function Footer() {
6 | return (
7 |
10 |
17 |
18 | )
19 | }
20 |
21 | export default Footer
22 |
--------------------------------------------------------------------------------
/src/components/FormContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Container, Row, Col } from 'react-bootstrap'
3 |
4 | function FormContainer({ children }) {
5 | return (
6 |
7 |
8 |
9 | {children}
10 |
11 |
12 |
13 | )
14 | }
15 |
16 | export default FormContainer
17 |
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useDispatch, useSelector } from 'react-redux'
3 | import { Navbar, Nav, Container, NavDropdown } from 'react-bootstrap'
4 | import { LinkContainer } from 'react-router-bootstrap'
5 | //import SearchBox from './SearchBox'
6 | import { logout } from '../actions/userActions'
7 |
8 |
9 | function Header() {
10 |
11 | const userLogin = useSelector(state => state.userLogin)
12 | const { userInfo } = userLogin
13 |
14 | const dispatch = useDispatch()
15 |
16 | const logoutHandler = () => {
17 | dispatch(logout())
18 | }
19 |
20 | return (
21 |
22 |
23 |
24 |
25 | AfroShopMe
26 |
27 |
28 |
29 |
30 |
84 |
85 |
86 |
87 |
88 | )
89 | }
90 |
91 | export default Header
92 |
--------------------------------------------------------------------------------
/src/components/Loader.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Spinner } from 'react-bootstrap'
3 |
4 |
5 | function Loader() {
6 | return (
7 |
17 | Loading...
18 |
19 | )
20 | }
21 |
22 | export default Loader
23 |
--------------------------------------------------------------------------------
/src/components/Message.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Alert } from 'react-bootstrap'
3 |
4 |
5 | function Message({ variant, children }) {
6 | return (
7 |
8 | {children}
9 |
10 | )
11 | }
12 |
13 | export default Message
--------------------------------------------------------------------------------
/src/components/Paginate.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Pagination } from 'react-bootstrap'
3 | import { LinkContainer } from 'react-router-bootstrap'
4 |
5 | function Paginate({ pages, page, keyword = '', isAdmin = false }) {
6 | if (keyword) {
7 | keyword = keyword.split('?keyword=')[1].split('&')[1].split('&')[0]
8 | }
9 |
10 | return (pages > 1 && (
11 |
12 | {[...Array(pages).keys()].map((x) => (
13 |
20 | {x + 1}
21 |
22 | ))}
23 |
24 | )
25 | )
26 | }
27 |
28 | export default Paginate
29 |
--------------------------------------------------------------------------------
/src/components/PaginateShop.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Pagination } from 'react-bootstrap'
3 | import { LinkContainer } from 'react-router-bootstrap'
4 |
5 | function PaginateShop({ pages, page, keyword = '', isAdmin = false }) {
6 | if (keyword) {
7 | keyword = keyword.split('?keyword=')[1].split('&')[1].split('&')[0]
8 | }
9 |
10 | return (pages > 1 && (
11 |
12 | {[...Array(pages).keys()].map((x) => (
13 |
20 | {x + 1}
21 |
22 | ))}
23 |
24 | )
25 | )
26 | }
27 |
28 | export default PaginateShop
29 |
--------------------------------------------------------------------------------
/src/components/Product.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Card, ListGroup } from 'react-bootstrap'
3 | import Rating from './Rating'
4 | import { Link } from 'react-router-dom'
5 |
6 |
7 | function Product({ product }) {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {product.name}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 | }
37 |
38 | export default Product
39 |
--------------------------------------------------------------------------------
/src/components/ProductCarousel.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { useDispatch, useSelector } from 'react-redux'
3 | import { Link } from 'react-router-dom'
4 | import { Carousel, Image } from 'react-bootstrap'
5 | import Loader from './Loader'
6 | import Message from './Message'
7 | import { listTopProducts } from '../actions/productActions'
8 |
9 | function ProductCarousel() {
10 | const dispatch = useDispatch()
11 |
12 | const productTopRated = useSelector(state => state.productTopRated)
13 | const { error, loading, products } = productTopRated
14 |
15 | useEffect(() => {
16 | dispatch(listTopProducts())
17 | }, [dispatch])
18 |
19 | return (loading ?
20 | : error
21 | ? {error}
22 | : (
23 |
24 | {products.map(product => (
25 |
26 |
27 |
28 |
29 | {product.name} (€{product.price})
30 |
31 |
32 |
33 | ))}
34 |
35 | )
36 |
37 | )
38 | }
39 |
40 | export default ProductCarousel
41 |
--------------------------------------------------------------------------------
/src/components/Rating.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function Rating({ value, text, color }) {
4 | return (
5 |
6 |
7 | = 1
9 | ? 'fas fa-star'
10 | : value >= 0.5
11 | ? 'fas fa-star-half-alt'
12 | : 'far fa-star'
13 | }>
14 |
15 |
16 |
17 |
18 |
19 | = 2
21 | ? 'fas fa-star'
22 | : value >= 1.5
23 | ? 'fas fa-star-half-alt'
24 | : 'far fa-star'
25 | }>
26 |
27 |
28 |
29 |
30 |
31 | = 3
33 | ? 'fas fa-star'
34 | : value >= 2.5
35 | ? 'fas fa-star-half-alt'
36 | : 'far fa-star'
37 | }>
38 |
39 |
40 |
41 |
42 |
43 | = 4
45 | ? 'fas fa-star'
46 | : value >= 3.5
47 | ? 'fas fa-star-half-alt'
48 | : 'far fa-star'
49 | }>
50 |
51 |
52 |
53 |
54 |
55 | = 5
57 | ? 'fas fa-star'
58 | : value >= 4.5
59 | ? 'fas fa-star-half-alt'
60 | : 'far fa-star'
61 | }>
62 |
63 |
64 |
65 |
66 | {text && text}
67 |
68 | )
69 | }
70 |
71 | export default Rating
72 |
--------------------------------------------------------------------------------
/src/components/SearchProduct.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { Button, Form } from 'react-bootstrap'
3 | import { useHistory } from 'react-router-dom'
4 |
5 | function SearchProduct() {
6 | const [keyword, setKeyword] = useState('')
7 |
8 | let history = useHistory()
9 |
10 | const submitHandler = (e) => {
11 | e.preventDefault()
12 | if (keyword) {
13 | history.push(`/result_products/?keyword=${keyword}&page=1`)
14 | } else {
15 | history.push(history.push(history.location.pathname))
16 | }
17 | }
18 | return (
19 | setKeyword(e.target.value)}
26 | style={{ width: "250px" }}
27 | >
28 |
29 |
36 |
37 | )
38 | }
39 |
40 | export default SearchProduct
41 |
42 |
--------------------------------------------------------------------------------
/src/components/SearchShop.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { Button, Form } from 'react-bootstrap'
3 | import { useHistory } from 'react-router-dom'
4 |
5 | function SearchShop() {
6 | const [keyword, setKeyword] = useState('')
7 |
8 | let history = useHistory()
9 |
10 | const submitHandler = (e) => {
11 | e.preventDefault()
12 | if (keyword) {
13 | history.push(`/result_shops/?keyword=${keyword}&page=1`)
14 | } else {
15 | history.push(history.push(history.location.pathname))
16 | }
17 | }
18 | return (
19 | setKeyword(e.target.value)}
26 | style={{ width: "300px" }}
27 | >
28 |
29 |
36 |
37 | )
38 | }
39 |
40 | export default SearchShop
41 |
42 |
--------------------------------------------------------------------------------
/src/components/Shop.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Card } from 'react-bootstrap'
3 | import Rating from './Rating'
4 | import { Link } from 'react-router-dom'
5 |
6 |
7 | function Shop({ shop }) {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | {shop.name}
18 |
19 |
20 |
21 |
22 |
23 |
24 | {`${shop.numReviews} reviews`}
25 |
26 |
27 |
28 |
29 |
30 | {shop.city}
31 |
32 |
33 |
34 |
35 | )
36 | }
37 |
38 | export default Shop
39 |
--------------------------------------------------------------------------------
/src/components/ShopCarousel.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { useDispatch, useSelector } from 'react-redux'
3 | import { Link } from 'react-router-dom'
4 | import { Carousel, Image } from 'react-bootstrap'
5 | import Loader from './Loader'
6 | import Message from './Message'
7 | import { listTopShops } from '../actions/shopActions'
8 |
9 | function ShopCarousel() {
10 | const dispatch = useDispatch()
11 |
12 | const shopTopRated = useSelector(state => state.shopTopRated)
13 | const { error, loading, shops } = shopTopRated
14 |
15 | useEffect(() => {
16 | dispatch(listTopShops())
17 | }, [dispatch])
18 |
19 | return (loading ?
20 | : error
21 | ? {error}
22 | : (
23 |
24 | {shops.map(shop => (
25 |
26 |
27 |
28 |
29 | {shop.name} {shop.city}
30 |
31 |
32 |
33 | ))}
34 |
35 | )
36 |
37 | )
38 | }
39 |
40 | export default ShopCarousel
41 |
--------------------------------------------------------------------------------
/src/constants/cartConstants.js:
--------------------------------------------------------------------------------
1 | export const CART_ADD_ITEM = 'CART_ADD_ITEM'
2 | export const CART_REMOVE_ITEM = 'CART_REMOVE_ITEM'
3 |
4 | export const CART_SAVE_SHIPPING_ADDRESS = 'CART_SAVE_SHIPPING_ADDRESS'
5 |
6 | export const CART_SAVE_PAYMENT_METHOD = 'CART_SAVE_PAYMENT_METHOD'
7 |
8 | export const CART_CLEAR_ITEMS = 'CART_CLEAR_ITEMS'
--------------------------------------------------------------------------------
/src/constants/orderConstants.js:
--------------------------------------------------------------------------------
1 | export const ORDER_CREATE_REQUEST = 'ORDER_CREATE_REQUEST'
2 | export const ORDER_CREATE_SUCCESS = 'ORDER_CREATE_SUCCESS'
3 | export const ORDER_CREATE_FAIL = 'ORDER_CREATE_FAIL'
4 |
5 | export const ORDER_CREATE_RESET = 'ORDER_CREATE_RESET'
6 |
7 | export const ORDER_DETAILS_REQUEST = 'ORDER_DETAILS_REQUEST'
8 | export const ORDER_DETAILS_SUCCESS = 'ORDER_DETAILS_SUCCESS'
9 | export const ORDER_DETAILS_FAIL = 'ORDER_DETAILS_FAIL'
10 |
11 | export const ORDER_PAY_REQUEST = 'ORDER_PAY_REQUEST'
12 | export const ORDER_PAY_SUCCESS = 'ORDER_PAY_SUCCESS'
13 | export const ORDER_PAY_FAIL = 'ORDER_PAY_FAIL'
14 | export const ORDER_PAY_RESET = 'ORDER_PAY_RESET'
15 |
16 | export const ORDER_LIST_MY_REQUEST = 'ORDER_LIST_MY_REQUEST'
17 | export const ORDER_LIST_MY_SUCCESS = 'ORDER_LIST_MY_SUCCESS'
18 | export const ORDER_LIST_MY_FAIL = 'ORDER_LIST_MY_FAIL'
19 | export const ORDER_LIST_MY_RESET = 'ORDER_LIST_MY_RESET'
20 |
21 |
22 | export const ORDER_LIST_REQUEST = 'ORDER_LIST_REQUEST'
23 | export const ORDER_LIST_SUCCESS = 'ORDER_LIST_SUCCESS'
24 | export const ORDER_LIST_FAIL = 'ORDER_LIST_FAIL'
25 |
26 |
27 | export const ORDER_DELIVER_REQUEST = 'ORDER_DELIVER_REQUEST'
28 | export const ORDER_DELIVER_SUCCESS = 'ORDER_DELIVER_SUCCESS'
29 | export const ORDER_DELIVER_FAIL = 'ORDER_DELIVER_FAIL'
30 | export const ORDER_DELIVER_RESET = 'ORDER_DELIVER_RESET'
31 |
--------------------------------------------------------------------------------
/src/constants/productConstants.js:
--------------------------------------------------------------------------------
1 | export const PRODUCT_LIST_REQUEST = 'PRODUCT_LIST_REQUEST'
2 | export const PRODUCT_LIST_SUCCESS = 'PRODUCT_LIST_SUCCESS'
3 | export const PRODUCT_LIST_FAIL = 'PRODUCT_LIST_FAIL'
4 |
5 | export const PRODUCT_DETAILS_REQUEST = 'PRODUCT_DETAILS_REQUEST'
6 | export const PRODUCT_DETAILS_SUCCESS = 'PRODUCT_DETAILS_SUCCESS'
7 | export const PRODUCT_DETAILS_FAIL = 'PRODUCT_DETAILS_FAIL'
8 |
9 | export const PRODUCT_DELETE_REQUEST = 'PRODUCT_DELETE_REQUEST'
10 | export const PRODUCT_DELETE_SUCCESS = 'PRODUCT_DELETE_SUCCESS'
11 | export const PRODUCT_DELETE_FAIL = 'PRODUCT_DELETE_FAIL'
12 |
13 | export const PRODUCT_CREATE_REQUEST = 'PRODUCT_CREATE_REQUEST'
14 | export const PRODUCT_CREATE_SUCCESS = 'PRODUCT_CREATE_SUCCESS'
15 | export const PRODUCT_CREATE_FAIL = 'PRODUCT_CREATE_FAIL'
16 | export const PRODUCT_CREATE_RESET = 'PRODUCT_CREATE_RESET'
17 |
18 |
19 | export const PRODUCT_UPDATE_REQUEST = 'PRODUCT_UPDATE_REQUEST'
20 | export const PRODUCT_UPDATE_SUCCESS = 'PRODUCT_UPDATE_SUCCESS'
21 | export const PRODUCT_UPDATE_FAIL = 'PRODUCT_UPDATE_FAIL'
22 | export const PRODUCT_UPDATE_RESET = 'PRODUCT_UPDATE_RESET'
23 |
24 | export const PRODUCT_CREATE_REVIEW_REQUEST = 'PRODUCT_CREATE_REVIEW_REQUEST'
25 | export const PRODUCT_CREATE_REVIEW_SUCCESS = 'PRODUCT_CREATE_REVIEW_SUCCESS'
26 | export const PRODUCT_CREATE_REVIEW_FAIL = 'PRODUCT_CREATE_REVIEW_FAIL'
27 | export const PRODUCT_CREATE_REVIEW_RESET = 'PRODUCT_CREATE_REVIEW_RESET'
28 |
29 | export const PRODUCT_TOP_REQUEST = 'PRODUCT_TOP_REQUEST'
30 | export const PRODUCT_TOP_SUCCESS = 'PRODUCT_TOP_SUCCESS'
31 | export const PRODUCT_TOP_FAIL = 'PRODUCT_TOP_FAIL'
32 |
--------------------------------------------------------------------------------
/src/constants/shopConstants.js:
--------------------------------------------------------------------------------
1 | export const SHOP_LIST_REQUEST = 'SHOP_LIST_REQUEST'
2 | export const SHOP_LIST_SUCCESS = 'SHOP_LIST_SUCCESS'
3 | export const SHOP_LIST_FAIL = 'SHOP_LIST_FAIL'
4 |
5 | export const SHOP_DETAILS_REQUEST = 'SHOP_DETAILS_REQUEST'
6 | export const SHOP_DETAILS_SUCCESS = 'SHOP_DETAILS_SUCCESS'
7 | export const SHOP_DETAILS_FAIL = 'SHOP_DETAILS_FAIL'
8 |
9 | export const SHOP_CREATE_REVIEW_REQUEST = 'SHOP_CREATE_REVIEW_REQUEST'
10 | export const SHOP_CREATE_REVIEW_SUCCESS = 'SHOP_CREATE_REVIEW_SUCCESS'
11 | export const SHOP_CREATE_REVIEW_FAIL = 'SHOP_CREATE_REVIEW_FAIL'
12 | export const SHOP_CREATE_REVIEW_RESET = 'SHOP_CREATE_REVIEW_RESET'
13 |
14 | export const SHOP_TOP_REQUEST = 'SHOP_TOP_REQUEST'
15 | export const SHOP_TOP_SUCCESS = 'SHOP_TOP_SUCCESS'
16 | export const SHOP_TOP_FAIL = 'SHOP_TOP_FAIL'
--------------------------------------------------------------------------------
/src/constants/userConstants.js:
--------------------------------------------------------------------------------
1 | export const USER_LOGIN_REQUEST = 'USER_LOGIN_REQUEST'
2 | export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS'
3 | export const USER_LOGIN_FAIL = 'USER_LOGIN_FAIL'
4 |
5 | export const USER_LOGOUT = 'USER_LOGOUT'
6 |
7 | export const USER_REGISTER_REQUEST = 'USER_REGISTER_REQUEST'
8 | export const USER_REGISTER_SUCCESS = 'USER_REGISTER_SUCCESS'
9 | export const USER_REGISTER_FAIL = 'USER_REGISTER_FAIL'
10 |
11 | export const USER_DETAILS_REQUEST = 'USER_DETAILS_REQUEST'
12 | export const USER_DETAILS_SUCCESS = 'USER_DETAILS_SUCCESS'
13 | export const USER_DETAILS_FAIL = 'USER_DETAILS_FAIL'
14 | export const USER_DETAILS_RESET = 'USER_DETAILS_RESET'
15 |
16 | export const USER_UPDATE_PROFILE_REQUEST = 'USER_UPDATE_PROFILE_REQUEST'
17 | export const USER_UPDATE_PROFILE_SUCCESS = 'USER_UPDATE_PROFILE_SUCCESS'
18 | export const USER_UPDATE_PROFILE_FAIL = 'USER_UPDATE_PROFILE_FAIL'
19 | export const USER_UPDATE_PROFILE_RESET = 'USER_UPDATE_PROFILE_RESET'
20 |
21 | export const USER_LIST_REQUEST = 'USER_LIST_REQUEST'
22 | export const USER_LIST_SUCCESS = 'USER_LIST_SUCCESS'
23 | export const USER_LIST_FAIL = 'USER_LIST_FAIL'
24 | export const USER_LIST_RESET = 'USER_LIST_RESET'
25 |
26 | export const USER_DELETE_REQUEST = 'USER_DELETE_REQUEST'
27 | export const USER_DELETE_SUCCESS = 'USER_DELETE_SUCCESS'
28 | export const USER_DELETE_FAIL = 'USER_DELETE_FAIL'
29 |
30 | export const USER_UPDATE_REQUEST = 'USER_UPDATE_REQUEST'
31 | export const USER_UPDATE_SUCCESS = 'USER_UPDATE_SUCCESS'
32 | export const USER_UPDATE_FAIL = 'USER_UPDATE_FAIL'
33 | export const USER_UPDATE_RESET = 'USER_UPDATE_RESET'
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | main{
2 | min-height: 85vh;
3 |
4 | }
5 |
6 | .rating span{
7 | margin:0.1rem;
8 | }
9 |
10 | h1{
11 | font-size: 1.8rem;
12 | padding: 1rem 0;
13 | }
14 |
15 | h2{
16 | font-size: 1.4rem;
17 | padding: 0.5rem 0;
18 | }
19 |
20 |
21 | h3{
22 | padding:1rem 0;
23 | }
24 |
25 |
26 | .list{
27 | margin: 0;
28 | padding: 0;
29 | list-style: none;
30 | text-align: center;
31 |
32 | padding: 0.6rem;
33 | border: solid 1px #f0f0f0;
34 | }
35 |
36 |
37 | .centered {
38 | display: flex;
39 | flex-direction: column;
40 | justify-content: center;
41 | align-items: center;
42 | text-align: center;
43 | min-height: 80vh;
44 | }
45 |
46 | .search{
47 | padding: 50px;
48 | position: relative;
49 | left: auto;
50 |
51 | }
52 |
53 | /* carousel */
54 | .carousel-item-next,
55 | .carousel-item-prev,
56 | .carousel-item.active {
57 | display: flex;
58 | }
59 | .carousel-caption {
60 | position: absolute;
61 | top: 0;
62 | }
63 |
64 | .carousel-caption h4 {
65 | color: #fff;
66 | }
67 |
68 | .carousel img {
69 | display:block;
70 | height: 300px;
71 | padding: 30px;
72 | margin: 40px;
73 | border-radius: 50%;
74 | margin-left: auto;
75 | margin-right: auto;
76 | }
77 | .carousel a {
78 | margin: 0 auto;
79 | }
80 | @media (max-width: 900px) {
81 | .carousel-caption h2 {
82 | font-size: 2.5vw;
83 | }
84 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Provider } from 'react-redux'
4 | import store from './store'
5 | import './index.css';
6 | import './bootstrap.min.css'
7 | import App from './App';
8 | import reportWebVitals from './reportWebVitals';
9 |
10 |
11 | // React.Render replaces by Provider passing the store
12 | ReactDOM.render(
13 |
14 |
15 | ,
16 | document.getElementById('root')
17 | );
18 |
19 |
20 | // If you want to start measuring performance in your app, pass a function
21 | // to log results (for example: reportWebVitals(console.log))
22 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
23 | reportWebVitals();
24 |
--------------------------------------------------------------------------------
/src/products.js:
--------------------------------------------------------------------------------
1 | const products = [
2 | {
3 | '_id': '1',
4 | 'name': 'Bobolo',
5 | 'image': '/images/Bobolo.png',
6 | 'brand': 'AfroShopMe',
7 | 'category': 'Tubercules',
8 | 'description':'No description for now - Coming soon!',
9 | 'rating': 4.5,
10 | 'numReviews': 10,
11 | 'price': 4.50,
12 | 'countInStock': 10,
13 | },
14 | {
15 | '_id': '2',
16 | 'name': 'Cacahuètes',
17 | 'image': '/images/arachide.png',
18 | 'brand': 'AfroShopMe',
19 | 'category': 'Arachides',
20 | 'description':'No description for now - Coming soon!',
21 | 'rating': 2.5,
22 | 'numReviews': 3,
23 | 'price': 4.50,
24 | 'countInStock': 18,
25 | },
26 | {
27 | '_id': '3',
28 | 'name': 'Tubercules de Manioc',
29 | 'image': '/images/manioc.png',
30 | 'brand': 'AfroShopMe',
31 | 'category': 'Tubercules',
32 | 'description':'No description for now - Coming soon!',
33 | 'rating': 4.5,
34 | 'numReviews': 11,
35 | 'price': 4.50,
36 | 'countInStock': 25,
37 | },
38 | {
39 | '_id': '4',
40 | 'name': 'Nkui',
41 | 'image': '/images/nkui.png',
42 | 'brand': 'AfroShopMe',
43 | 'category': 'Epices',
44 | 'description':'No description for now - Coming soon!',
45 | 'rating': 4.0,
46 | 'numReviews': 21,
47 | 'price': 4.50,
48 | 'countInStock': 5,
49 | },
50 | {
51 | '_id': '5',
52 | 'name': 'Okok',
53 | 'image': '/images/okok.png',
54 | 'brand': 'AfroShopMe',
55 | 'category': 'Legumes',
56 | 'description':'No description for now - Coming soon!',
57 | 'rating': 5.0,
58 | 'numReviews': 13,
59 | 'price': 4.50,
60 | 'countInStock': 19,
61 | },
62 | {
63 | '_id': '6',
64 | 'name': 'Huile de palme',
65 | 'image': '/images/palmoil.png',
66 | 'brand': 'AfroShopMe',
67 | 'category': 'Oléagineux',
68 | 'description':'No description for now - Coming soon!',
69 | 'rating': 4.0,
70 | 'numReviews': 18,
71 | 'price': 4.50,
72 | 'countInStock': 22,
73 | },
74 | {
75 | '_id': '7',
76 | 'name': '4 Côtés',
77 | 'image': '/images/tetrapleura.png',
78 | 'brand': 'AfroShopMe',
79 | 'category': 'Epices',
80 | 'description':'No description for now - Coming soon!',
81 | 'rating': 4.0,
82 | 'numReviews': 2,
83 | 'price': 4.50,
84 | 'countInStock': 35,
85 | },
86 | {
87 | '_id': '8',
88 | 'name': 'Igname',
89 | 'image': '/images/yam.png',
90 | 'brand': 'AfroShopMe',
91 | 'category': 'Tubercules',
92 | 'description':'No description for now - Coming soon!',
93 | 'rating': 4.5,
94 | 'numReviews': 15,
95 | 'price': 4.50,
96 | 'countInStock': 1,
97 | },
98 | {
99 | '_id': '9',
100 | 'name': 'Rondelles',
101 | 'image': '/images/rondelle.png',
102 | 'brand': 'AfroShopMe',
103 | 'category': 'Epices',
104 | 'description':'No description for now - Coming soon!',
105 | 'rating': 3.5,
106 | 'numReviews': 20,
107 | 'price': 4.50,
108 | 'countInStock': 150,
109 | },
110 | ]
111 |
112 | export default products
113 |
114 |
--------------------------------------------------------------------------------
/src/reducers/cartReducers.js:
--------------------------------------------------------------------------------
1 | import {
2 | CART_ADD_ITEM,
3 | CART_REMOVE_ITEM,
4 | CART_SAVE_SHIPPING_ADDRESS,
5 |
6 | CART_SAVE_PAYMENT_METHOD,
7 |
8 | CART_CLEAR_ITEMS,
9 | } from '../constants/cartConstants'
10 |
11 |
12 |
13 | export const cartReducer = (state = { cartItems: [] }, action) => {
14 | switch (action.type) {
15 | case CART_ADD_ITEM:
16 | const item = action.payload
17 | const existItem = state.cartItems.find(x => x.product === item.product)
18 |
19 | if (existItem) {
20 | return {
21 | ...state,
22 | cartItems: state.cartItems.map(x =>
23 | x.product === existItem.product ? item : x)
24 | }
25 |
26 | } else {
27 | return {
28 | ...state,
29 | cartItems: [...state.cartItems, item]
30 | }
31 | }
32 |
33 | case CART_REMOVE_ITEM:
34 | return {
35 | ...state,
36 | cartItems: state.cartItems.filter(x => x.product !== action.payload)
37 | }
38 |
39 | case CART_SAVE_SHIPPING_ADDRESS:
40 | return {
41 | ...state,
42 | shippingAddress: action.payload
43 | }
44 |
45 | case CART_SAVE_PAYMENT_METHOD:
46 | return {
47 | ...state,
48 | paymentMethod: action.payload
49 | }
50 |
51 | case CART_CLEAR_ITEMS:
52 | return {
53 | ...state,
54 | cartItems: []
55 | }
56 |
57 | default:
58 | return state
59 | }
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/src/reducers/orderReducers.js:
--------------------------------------------------------------------------------
1 | import {
2 | ORDER_CREATE_REQUEST,
3 | ORDER_CREATE_SUCCESS,
4 | ORDER_CREATE_FAIL,
5 |
6 | ORDER_CREATE_RESET,
7 |
8 | ORDER_DETAILS_REQUEST,
9 | ORDER_DETAILS_SUCCESS,
10 | ORDER_DETAILS_FAIL,
11 |
12 | ORDER_PAY_REQUEST,
13 | ORDER_PAY_SUCCESS,
14 | ORDER_PAY_FAIL,
15 | ORDER_PAY_RESET,
16 |
17 | ORDER_LIST_MY_REQUEST,
18 | ORDER_LIST_MY_SUCCESS,
19 | ORDER_LIST_MY_FAIL,
20 | ORDER_LIST_MY_RESET,
21 |
22 | ORDER_LIST_REQUEST,
23 | ORDER_LIST_SUCCESS,
24 | ORDER_LIST_FAIL,
25 |
26 | ORDER_DELIVER_REQUEST,
27 | ORDER_DELIVER_SUCCESS,
28 | ORDER_DELIVER_FAIL,
29 | ORDER_DELIVER_RESET,
30 | } from '../constants/orderConstants'
31 |
32 |
33 | export const orderCreateReducer = (state = {}, action) => {
34 | switch (action.type) {
35 | case ORDER_CREATE_REQUEST:
36 | return {
37 | loading: true
38 | }
39 |
40 | case ORDER_CREATE_SUCCESS:
41 | return {
42 | loading: false,
43 | success: true,
44 | order: action.payload
45 | }
46 |
47 | case ORDER_CREATE_FAIL:
48 | return {
49 | loading: false,
50 | error: action.payload
51 | }
52 |
53 | case ORDER_CREATE_RESET:
54 | return {}
55 |
56 |
57 | default:
58 | return state
59 | }
60 | }
61 |
62 |
63 | export const orderDetailsReducer = (state = { loading: true, orderItems: [], shippingAddress: {} }, action) => {
64 | switch (action.type) {
65 | case ORDER_DETAILS_REQUEST:
66 | return {
67 | ...state,
68 | loading: true
69 | }
70 |
71 | case ORDER_DETAILS_SUCCESS:
72 | return {
73 | loading: false,
74 | order: action.payload
75 | }
76 |
77 | case ORDER_DETAILS_FAIL:
78 | return {
79 | loading: false,
80 | error: action.payload
81 | }
82 |
83 |
84 | default:
85 | return state
86 | }
87 | }
88 |
89 |
90 | export const orderPayReducer = (state = {}, action) => {
91 | switch (action.type) {
92 | case ORDER_PAY_REQUEST:
93 | return {
94 | loading: true
95 | }
96 |
97 | case ORDER_PAY_SUCCESS:
98 | return {
99 | loading: false,
100 | success: true
101 | }
102 |
103 | case ORDER_PAY_FAIL:
104 | return {
105 | loading: false,
106 | error: action.payload
107 | }
108 |
109 | case ORDER_PAY_RESET:
110 | return {}
111 |
112 | default:
113 | return state
114 | }
115 | }
116 |
117 |
118 | export const orderListMyReducer = (state = { orders: [] }, action) => {
119 | switch (action.type) {
120 | case ORDER_LIST_MY_REQUEST:
121 | return {
122 | loading: true
123 | }
124 |
125 | case ORDER_LIST_MY_SUCCESS:
126 | return {
127 | loading: false,
128 | orders: action.payload
129 | }
130 |
131 | case ORDER_LIST_MY_FAIL:
132 | return {
133 | loading: false,
134 | error: action.payload
135 | }
136 |
137 | case ORDER_LIST_MY_RESET:
138 | return {
139 | orders: []
140 | }
141 |
142 | default:
143 | return state
144 | }
145 | }
146 |
147 |
148 | export const orderListReducer = (state = { orders: [] }, action) => {
149 | switch (action.type) {
150 | case ORDER_LIST_REQUEST:
151 | return {
152 | loading: true
153 | }
154 |
155 | case ORDER_LIST_SUCCESS:
156 | return {
157 | loading: false,
158 | orders: action.payload
159 | }
160 |
161 | case ORDER_LIST_FAIL:
162 | return {
163 | loading: false,
164 | error: action.payload
165 | }
166 | default:
167 | return state
168 | }
169 | }
170 |
171 |
172 | export const orderDeliverReducer = (state = {}, action) => {
173 | switch (action.type) {
174 | case ORDER_DELIVER_REQUEST:
175 | return {
176 | loading: true
177 | }
178 |
179 | case ORDER_DELIVER_SUCCESS:
180 | return {
181 | loading: false,
182 | success: true
183 | }
184 |
185 | case ORDER_DELIVER_FAIL:
186 | return {
187 | loading: false,
188 | error: action.payload
189 | }
190 |
191 | case ORDER_DELIVER_RESET:
192 | return {}
193 |
194 | default:
195 | return state
196 | }
197 | }
198 |
199 |
--------------------------------------------------------------------------------
/src/reducers/productReducers.js:
--------------------------------------------------------------------------------
1 | import {
2 | PRODUCT_LIST_REQUEST,
3 | PRODUCT_LIST_SUCCESS,
4 | PRODUCT_LIST_FAIL,
5 |
6 | PRODUCT_DETAILS_REQUEST,
7 | PRODUCT_DETAILS_SUCCESS,
8 | PRODUCT_DETAILS_FAIL,
9 |
10 | PRODUCT_DELETE_REQUEST,
11 | PRODUCT_DELETE_SUCCESS,
12 | PRODUCT_DELETE_FAIL,
13 |
14 | PRODUCT_CREATE_REQUEST,
15 | PRODUCT_CREATE_SUCCESS,
16 | PRODUCT_CREATE_FAIL,
17 | PRODUCT_CREATE_RESET,
18 |
19 | PRODUCT_UPDATE_REQUEST,
20 | PRODUCT_UPDATE_SUCCESS,
21 | PRODUCT_UPDATE_FAIL,
22 | PRODUCT_UPDATE_RESET,
23 |
24 | PRODUCT_CREATE_REVIEW_REQUEST,
25 | PRODUCT_CREATE_REVIEW_SUCCESS,
26 | PRODUCT_CREATE_REVIEW_FAIL,
27 | PRODUCT_CREATE_REVIEW_RESET,
28 |
29 | PRODUCT_TOP_REQUEST,
30 | PRODUCT_TOP_SUCCESS,
31 | PRODUCT_TOP_FAIL,
32 | } from '../constants/productConstants'
33 |
34 | export const productListReducer = (state = { products: [] }, action) => {
35 | switch(action.type){
36 | case PRODUCT_LIST_REQUEST:
37 | return { loading: true, products: [] }
38 |
39 | case PRODUCT_LIST_SUCCESS:
40 | return {
41 | loading: false,
42 | products: action.payload.products,
43 | page: action.payload.products,
44 | pages: action.payload.pages
45 | }
46 |
47 | case PRODUCT_LIST_FAIL:
48 | return { loading: false, error: action.payload }
49 |
50 | default:
51 | return state
52 | }
53 | }
54 |
55 | export const productDetailsReducer = (state = { product: {reviews:[]} }, action) => {
56 | switch(action.type){
57 | case PRODUCT_DETAILS_REQUEST:
58 | return { loading: true, ...state}
59 |
60 | case PRODUCT_DETAILS_SUCCESS:
61 | return { loading: false, product: action.payload}
62 |
63 | case PRODUCT_DETAILS_FAIL:
64 | return { loading: false, error: action.payload }
65 |
66 | default:
67 | return state
68 | }
69 | }
70 |
71 |
72 | export const productDeleteReducer = (state = {}, action) => {
73 | switch (action.type) {
74 | case PRODUCT_DELETE_REQUEST:
75 | return { loading: true }
76 |
77 | case PRODUCT_DELETE_SUCCESS:
78 | return { loading: false, success: true }
79 |
80 | case PRODUCT_DELETE_FAIL:
81 | return { loading: false, error: action.payload }
82 |
83 | default:
84 | return state
85 | }
86 | }
87 |
88 |
89 | export const productCreateReducer = (state = {}, action) => {
90 | switch (action.type) {
91 | case PRODUCT_CREATE_REQUEST:
92 | return { loading: true }
93 |
94 | case PRODUCT_CREATE_SUCCESS:
95 | return { loading: false, success: true, product: action.payload }
96 |
97 | case PRODUCT_CREATE_FAIL:
98 | return { loading: false, error: action.payload }
99 |
100 | case PRODUCT_CREATE_RESET:
101 | return {}
102 |
103 | default:
104 | return state
105 | }
106 | }
107 |
108 |
109 | export const productUpdateReducer = (state = { product: {} }, action) => {
110 | switch (action.type) {
111 | case PRODUCT_UPDATE_REQUEST:
112 | return { loading: true }
113 |
114 | case PRODUCT_UPDATE_SUCCESS:
115 | return { loading: false, success: true, product: action.payload }
116 |
117 | case PRODUCT_UPDATE_FAIL:
118 | return { loading: false, error: action.payload }
119 |
120 | case PRODUCT_UPDATE_RESET:
121 | return { product: {} }
122 |
123 | default:
124 | return state
125 | }
126 | }
127 |
128 | export const productReviewCreateReducer = (state = {}, action) => {
129 | switch (action.type) {
130 | case PRODUCT_CREATE_REVIEW_REQUEST:
131 | return { loading: true }
132 |
133 | case PRODUCT_CREATE_REVIEW_SUCCESS:
134 | return { loading: false, success: true, }
135 |
136 | case PRODUCT_CREATE_REVIEW_FAIL:
137 | return { loading: false, error: action.payload }
138 |
139 | case PRODUCT_CREATE_REVIEW_RESET:
140 | return {}
141 |
142 | default:
143 | return state
144 | }
145 | }
146 |
147 |
148 | export const productTopRatedReducer = (state = { products: [] }, action) => {
149 | switch (action.type) {
150 | case PRODUCT_TOP_REQUEST:
151 | return { loading: true, products: [] }
152 |
153 | case PRODUCT_TOP_SUCCESS:
154 | return { loading: false, products: action.payload, }
155 |
156 | case PRODUCT_TOP_FAIL:
157 | return { loading: false, error: action.payload }
158 |
159 | default:
160 | return state
161 | }
162 | }
--------------------------------------------------------------------------------
/src/reducers/shopReducers.js:
--------------------------------------------------------------------------------
1 | import {
2 | SHOP_LIST_REQUEST,
3 | SHOP_LIST_SUCCESS,
4 | SHOP_LIST_FAIL,
5 |
6 | SHOP_DETAILS_REQUEST,
7 | SHOP_DETAILS_SUCCESS,
8 | SHOP_DETAILS_FAIL,
9 |
10 | SHOP_CREATE_REVIEW_REQUEST,
11 | SHOP_CREATE_REVIEW_SUCCESS,
12 | SHOP_CREATE_REVIEW_FAIL,
13 | SHOP_CREATE_REVIEW_RESET,
14 |
15 | SHOP_TOP_REQUEST,
16 | SHOP_TOP_SUCCESS,
17 | SHOP_TOP_FAIL,
18 | } from '../constants/shopConstants'
19 |
20 | export const shopListReducer = (state = { shops: [] }, action) => {
21 | switch(action.type){
22 | case SHOP_LIST_REQUEST:
23 | return { loading: true, shops: [] }
24 |
25 | case SHOP_LIST_SUCCESS:
26 | return {
27 | loading: false,
28 | shops: action.payload.shops,
29 | page: action.payload.shops,
30 | pages: action.payload.pages
31 | }
32 |
33 | case SHOP_LIST_FAIL:
34 | return { loading: false, error: action.payload }
35 |
36 | default:
37 | return state
38 | }
39 | }
40 |
41 |
42 | export const shopDetailsReducer = (state = { shop: {reviews:[]} }, action) => {
43 | switch(action.type){
44 | case SHOP_DETAILS_REQUEST:
45 | return { loading: true, ...state }
46 |
47 | case SHOP_DETAILS_SUCCESS:
48 | return { loading: false, shop: action.payload}
49 |
50 | case SHOP_DETAILS_FAIL:
51 | return { loading: false, error: action.payload }
52 |
53 | default:
54 | return state
55 | }
56 | }
57 |
58 |
59 | export const shopReviewCreateReducer = (state = {}, action) => {
60 | switch (action.type) {
61 | case SHOP_CREATE_REVIEW_REQUEST:
62 | return { loading: true }
63 |
64 | case SHOP_CREATE_REVIEW_SUCCESS:
65 | return { loading: false, success: true, }
66 |
67 | case SHOP_CREATE_REVIEW_FAIL:
68 | return { loading: false, error: action.payload }
69 |
70 | case SHOP_CREATE_REVIEW_RESET:
71 | return {}
72 |
73 | default:
74 | return state
75 | }
76 | }
77 |
78 |
79 | export const shopTopRatedReducer = (state = { shops: [] }, action) => {
80 | switch (action.type) {
81 | case SHOP_TOP_REQUEST:
82 | return { loading: true, shops: [] }
83 |
84 | case SHOP_TOP_SUCCESS:
85 | return { loading: false, shops: action.payload, }
86 |
87 | case SHOP_TOP_FAIL:
88 | return { loading: false, error: action.payload }
89 |
90 | default:
91 | return state
92 | }
93 | }
--------------------------------------------------------------------------------
/src/reducers/userReducers.js:
--------------------------------------------------------------------------------
1 | import {
2 | USER_LOGIN_REQUEST,
3 | USER_LOGIN_SUCCESS,
4 | USER_LOGIN_FAIL,
5 |
6 | USER_LOGOUT,
7 |
8 | USER_REGISTER_REQUEST,
9 | USER_REGISTER_SUCCESS,
10 | USER_REGISTER_FAIL,
11 |
12 | USER_DETAILS_REQUEST,
13 | USER_DETAILS_SUCCESS,
14 | USER_DETAILS_FAIL,
15 | USER_DETAILS_RESET,
16 |
17 | USER_UPDATE_PROFILE_REQUEST,
18 | USER_UPDATE_PROFILE_SUCCESS,
19 | USER_UPDATE_PROFILE_FAIL,
20 | USER_UPDATE_PROFILE_RESET,
21 |
22 | USER_LIST_REQUEST,
23 | USER_LIST_SUCCESS,
24 | USER_LIST_FAIL,
25 | USER_LIST_RESET,
26 |
27 | USER_DELETE_REQUEST,
28 | USER_DELETE_SUCCESS,
29 | USER_DELETE_FAIL,
30 |
31 | USER_UPDATE_REQUEST,
32 | USER_UPDATE_SUCCESS,
33 | USER_UPDATE_FAIL,
34 | USER_UPDATE_RESET,
35 | } from '../constants/userConstants'
36 |
37 | export const userLoginReducer = (state = { }, action) => {
38 | switch(action.type){
39 | case USER_LOGIN_REQUEST:
40 | return { loading: true}
41 |
42 | case USER_LOGIN_SUCCESS:
43 | return { loading: false, userInfo: action.payload}
44 |
45 | case USER_LOGIN_FAIL:
46 | return { loading: false, error: action.payload }
47 |
48 | case USER_LOGOUT:
49 | return {}
50 |
51 | default:
52 | return state
53 | }
54 | }
55 |
56 |
57 | export const userRegisterReducer = (state = {}, action) => {
58 | switch (action.type) {
59 | case USER_REGISTER_REQUEST:
60 | return { loading: true }
61 |
62 | case USER_REGISTER_SUCCESS:
63 | return { loading: false, userInfo: action.payload }
64 |
65 | case USER_REGISTER_FAIL:
66 | return { loading: false, error: action.payload }
67 |
68 | case USER_LOGOUT:
69 | return {}
70 |
71 | default:
72 | return state
73 | }
74 | }
75 |
76 |
77 | export const userDetailsReducer = (state = { user: {} }, action) => {
78 | switch (action.type) {
79 | case USER_DETAILS_REQUEST:
80 | return { ...state, loading: true }
81 |
82 | case USER_DETAILS_SUCCESS:
83 | return { loading: false, user: action.payload }
84 |
85 | case USER_DETAILS_FAIL:
86 | return { loading: false, error: action.payload }
87 |
88 | case USER_DETAILS_RESET:
89 | return { user: {} }
90 |
91 |
92 | default:
93 | return state
94 | }
95 | }
96 |
97 |
98 | export const userUpdateProfileReducer = (state = {}, action) => {
99 | switch (action.type) {
100 | case USER_UPDATE_PROFILE_REQUEST:
101 | return { loading: true }
102 |
103 | case USER_UPDATE_PROFILE_SUCCESS:
104 | return { loading: false, success: true, userInfo: action.payload }
105 |
106 | case USER_UPDATE_PROFILE_FAIL:
107 | return { loading: false, error: action.payload }
108 |
109 | case USER_UPDATE_PROFILE_RESET:
110 | return {}
111 |
112 | default:
113 | return state
114 | }
115 | }
116 |
117 |
118 | export const userListReducer = (state = { users: [] }, action) => {
119 | switch (action.type) {
120 | case USER_LIST_REQUEST:
121 | return { loading: true }
122 |
123 | case USER_LIST_SUCCESS:
124 | return { loading: false, users: action.payload }
125 |
126 | case USER_LIST_FAIL:
127 | return { loading: false, error: action.payload }
128 |
129 | case USER_LIST_RESET:
130 | return { users: [] }
131 |
132 | default:
133 | return state
134 | }
135 | }
136 |
137 |
138 | export const userDeleteReducer = (state = {}, action) => {
139 | switch (action.type) {
140 | case USER_DELETE_REQUEST:
141 | return { loading: true }
142 |
143 | case USER_DELETE_SUCCESS:
144 | return { loading: false, success: true }
145 |
146 | case USER_DELETE_FAIL:
147 | return { loading: false, error: action.payload }
148 |
149 | default:
150 | return state
151 | }
152 | }
153 |
154 |
155 | export const userUpdateReducer = (state = { user: {} }, action) => {
156 | switch (action.type) {
157 | case USER_UPDATE_REQUEST:
158 | return { loading: true }
159 |
160 | case USER_UPDATE_SUCCESS:
161 | return { loading: false, success: true }
162 |
163 | case USER_UPDATE_FAIL:
164 | return { loading: false, error: action.payload }
165 |
166 | case USER_UPDATE_RESET:
167 | return { user: {} }
168 |
169 | default:
170 | return state
171 | }
172 | }
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/screens/CartScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { useDispatch, useSelector } from 'react-redux'
4 | import { Row, Col, ListGroup, Image, Form, Button, Card } from 'react-bootstrap'
5 | import Message from '../components/Message'
6 | import { addToCart, removeFromCart } from '../actions/cartActions'
7 |
8 |
9 | function CartScreen({ match, location, history }) {
10 | const productId = match.params.id
11 | const qty = location.search ? Number(location.search.split('=')[1]) : 1
12 |
13 | const dispatch = useDispatch()
14 |
15 | const cart = useSelector(state => state.cart)
16 | const { cartItems } = cart
17 |
18 | useEffect(() =>{
19 | if(productId) {
20 | dispatch(addToCart(productId, qty))
21 | }
22 | }, [dispatch, productId, qty])
23 |
24 | const removeFromCartHandler = (id) => {
25 | dispatch(removeFromCart(id))
26 | }
27 |
28 | const checkoutHandler = () => {
29 | history.push('/login_client?redirect=shipping')
30 | }
31 |
32 | return (
33 |
34 |
35 | Shopping Cart
36 | {/* {cartItems.length >= 0 ?(
37 | Add More Products
38 | )} */}
39 |
40 | {cartItems.length === 0 ? (
41 |
42 | Your cart is empty ... ! Go Back To Products
43 |
44 | ) : (
45 |
46 | {cartItems.map(item => (
47 |
48 |
49 |
50 |
51 |
52 |
53 | {item.name}
54 |
55 |
56 |
57 | ${item.price}
58 |
59 |
60 |
61 | dispatch(addToCart(item.product, Number(e.target.value)))}
65 | >
66 | {
67 |
68 | [...Array(item.countInStock).keys()].map((x) => (
69 |
72 | ))
73 | }
74 |
75 |
76 |
77 |
78 |
79 |
86 |
87 |
88 |
89 | ))}
90 |
91 | )}
92 |
93 |
94 |
95 |
96 |
97 |
98 | Subtotal ({cartItems.reduce((acc, item) => acc + item.qty, 0)}) items
99 | ${cartItems.reduce((acc, item) => acc + item.qty * item.price, 0).toFixed(2)}
100 |
101 |
102 |
103 |
104 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | )
119 | }
120 |
121 | export default CartScreen
122 |
--------------------------------------------------------------------------------
/src/screens/GrowBusinessScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function GrowBusinessScreen(history) {
4 |
5 | return (
6 |
7 |
Coming Soon ...
8 |
9 | )
10 | }
11 |
12 | export default GrowBusinessScreen
13 |
--------------------------------------------------------------------------------
/src/screens/HomeProductScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { useDispatch, useSelector } from 'react-redux'
3 | import { Row, Col } from 'react-bootstrap'
4 | import Product from '../components/Product'
5 | import Loader from '../components/Loader'
6 | import Message from '../components/Message'
7 | import Paginate from '../components/Paginate'
8 | import ProductCarousel from '../components/ProductCarousel'
9 | import { listProducts } from '../actions/productActions'
10 | import SearchProduct from '../components/SearchProduct'
11 |
12 |
13 |
14 | function HomeProductScreen({history}) {
15 | const dispatch = useDispatch()
16 | const productList = useSelector(state => state.productList)
17 | const { error, loading, products, page, pages } = productList
18 |
19 | let keyword = history.location.search
20 |
21 | useEffect(() => {
22 | dispatch(listProducts(keyword))
23 |
24 | }, [dispatch, keyword])
25 |
26 | return (
27 |
28 | {!keyword &&
}
29 |
30 |
Latest Products
31 |
32 |
33 | {loading ?
34 | : error ?
{error}
35 | :
36 |
37 |
38 | {products.map(product => (
39 |
40 |
41 |
42 | ))}
43 |
44 |
45 |
46 | }
47 |
48 |
49 | )
50 | }
51 |
52 | export default HomeProductScreen
53 |
--------------------------------------------------------------------------------
/src/screens/HomeScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { useDispatch, useSelector } from 'react-redux'
3 | import { Row, Col } from 'react-bootstrap'
4 | import Shop from '../components/Shop'
5 | import Loader from '../components/Loader'
6 | import Message from '../components/Message'
7 | import PaginateShop from '../components/PaginateShop'
8 | import ShopCarousel from '../components/ShopCarousel'
9 | import { listShops } from '../actions/shopActions'
10 | import SearchShop from '../components/SearchShop'
11 |
12 |
13 |
14 | function HomeScreen(history) {
15 | const dispatch = useDispatch()
16 | const shopList = useSelector(state => state.shopList)
17 | const { error, loading, shops, page, pages } = shopList
18 |
19 | let keyword = history.location.search
20 |
21 | useEffect(() => {
22 | dispatch(listShops(keyword))
23 |
24 | }, [dispatch, keyword])
25 |
26 | return (
27 |
28 | {!keyword &&
}
29 |
30 |
Latest Shops
31 |
32 |
33 | {loading ?
34 | : error ?
{error}
35 | :
36 |
37 |
38 | {shops.map(shop => (
39 |
40 |
41 |
42 | ))}
43 |
44 |
45 |
46 | }
47 |
48 |
49 | )
50 | }
51 |
52 | export default HomeScreen
53 |
--------------------------------------------------------------------------------
/src/screens/IndexSearchScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { useDispatch, useSelector } from 'react-redux'
3 | import { listShops } from '../actions/shopActions'
4 | import SearchShop from '../components/SearchShop'
5 | import { Image } from 'react-bootstrap'
6 |
7 |
8 | function IndexSearchScreen(history) {
9 | const dispatch = useDispatch()
10 | const shopList = useSelector(state => state.shopList)
11 | const { error, loading, shops } = shopList
12 |
13 | let keyword = history.location.search
14 |
15 | useEffect(() => {
16 | dispatch(listShops(keyword))
17 |
18 | }, [dispatch, keyword])
19 |
20 | return (
21 |
24 | {/*}
Search For a Shop and More ...
*/}
25 |
26 |
27 | )
28 | }
29 |
30 | export default IndexSearchScreen
31 |
--------------------------------------------------------------------------------
/src/screens/Index_Screen.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | //import { ImageBackground } from 'react-native'
3 | import { Row, Col, Image, ListGroup, Card } from 'react-bootstrap'
4 | import { Link } from 'react-router-dom'
5 |
6 |
7 | function Index_Screen() {
8 | return (
9 |
10 |
11 |
Welcome to AfroShopMe
12 | Find your AfroShop and more ...
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Looking for a special shop?
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | Or just start shopping here.
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | Open your own Store...
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | And grow your business.
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | )
71 | }
72 |
73 | export default Index_Screen
74 |
--------------------------------------------------------------------------------
/src/screens/LoginScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { Form, Button, Row, Col } from 'react-bootstrap'
4 | import { useDispatch, useSelector } from 'react-redux'
5 | import Loader from '../components/Loader'
6 | import Message from '../components/Message'
7 | import FormContainer from '../components/FormContainer'
8 | import { login } from '../actions/userActions'
9 |
10 | function LoginScreen({ location, history }) {
11 | const [email, setEmail] = useState('')
12 | const [password, setPassword] = useState('')
13 |
14 | const dispatch = useDispatch()
15 |
16 | const redirect = location.search ? location.search.split('=')[1] : '/'
17 |
18 | const userLogin = useSelector(state => state.userLogin)
19 | const { error, loading, userInfo } = userLogin
20 |
21 | useEffect(() => {
22 | if (userInfo) {
23 | history.push(redirect)
24 | }
25 | }, [history, userInfo, redirect])
26 |
27 | const submitHandler = (e) => {
28 | e.preventDefault()
29 | dispatch(login(email, password))
30 | }
31 |
32 | return (
33 |
34 | Sign In
35 | {error && {error}}
36 | {loading && }
37 |
40 | Email Address
41 | setEmail(e.target.value)}
46 | >
47 |
48 |
49 |
50 |
51 |
52 | Password
53 | setPassword(e.target.value)}
58 | >
59 |
60 |
61 |
62 |
65 |
66 |
67 |
68 |
69 | New Customer?
71 | Register
72 |
73 |
74 |
75 |
76 |
77 | )
78 | }
79 |
80 | export default LoginScreen
81 |
--------------------------------------------------------------------------------
/src/screens/OpenYourStoreScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function OpenYourStoreScreen(history) {
4 |
5 | return (
6 |
7 |
Coming Soon ...
8 |
9 | )
10 | }
11 |
12 | export default OpenYourStoreScreen
13 |
--------------------------------------------------------------------------------
/src/screens/OrderListScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { LinkContainer } from 'react-router-bootstrap'
3 | import { Table, Button } from 'react-bootstrap'
4 | import { useDispatch, useSelector } from 'react-redux'
5 | import Loader from '../components/Loader'
6 | import Message from '../components/Message'
7 | import { listOrders } from '../actions/orderActions'
8 |
9 | function OrderListScreen({ history }) {
10 |
11 | const dispatch = useDispatch()
12 |
13 | const orderList = useSelector(state => state.orderList)
14 | const { loading, error, orders } = orderList
15 |
16 | const userLogin = useSelector(state => state.userLogin)
17 | const { userInfo } = userLogin
18 |
19 |
20 |
21 | useEffect(() => {
22 | if (userInfo && userInfo.isAdmin) {
23 | dispatch(listOrders())
24 | } else {
25 | history.push('/login_client')
26 | }
27 |
28 | }, [dispatch, history, userInfo])
29 |
30 |
31 | return (
32 |
33 |
Orders
34 | {loading
35 | ? (
)
36 | : error
37 | ? (
{error})
38 | : (
39 |
40 |
41 |
42 | ID |
43 | USER |
44 | DATE |
45 | Total |
46 | PAID |
47 | DELIVERED |
48 | |
49 |
50 |
51 |
52 |
53 | {orders.map(order => (
54 |
55 | {order._id} |
56 | {order.user && order.user.name} |
57 | {order.createdAt.substring(0, 10)} |
58 | €{order.totalPrice} |
59 |
60 | {order.isPaid ? (
61 | order.paidAt.substring(0, 10)
62 | ) : (
63 |
64 | )}
65 | |
66 |
67 | {order.isDelivered ? (
68 | order.deliveredAt.substring(0, 10)
69 | ) : (
70 |
71 | )}
72 | |
73 |
74 |
75 |
76 |
79 |
80 |
81 |
82 | |
83 |
84 | ))}
85 |
86 |
87 | )}
88 |
89 | )
90 | }
91 |
92 | export default OrderListScreen
--------------------------------------------------------------------------------
/src/screens/OrderScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { Button, Row, Col, ListGroup, Image, Card } from 'react-bootstrap'
3 | import { Link } from 'react-router-dom'
4 | import { useDispatch, useSelector } from 'react-redux'
5 | import { PayPalButton } from 'react-paypal-button-v2'
6 | import Message from '../components/Message'
7 | import Loader from '../components/Loader'
8 | import { getOrderDetails, payOrder, deliverOrder } from '../actions/orderActions'
9 | import { ORDER_PAY_RESET, ORDER_DELIVER_RESET } from '../constants/orderConstants'
10 | import moment from 'moment'
11 |
12 |
13 | function OrderScreen({ match, history }) {
14 | const orderId = match.params.id
15 | const dispatch = useDispatch()
16 |
17 |
18 | const [sdkReady, setSdkReady] = useState(false)
19 |
20 | const orderDetails = useSelector(state => state.orderDetails)
21 | const { order, error, loading } = orderDetails
22 |
23 | const orderPay = useSelector(state => state.orderPay)
24 | const { loading: loadingPay, success: successPay } = orderPay
25 |
26 | const orderDeliver = useSelector(state => state.orderDeliver)
27 | const { loading: loadingDeliver, success: successDeliver } = orderDeliver
28 |
29 | const userLogin = useSelector(state => state.userLogin)
30 | const { userInfo } = userLogin
31 |
32 |
33 | if (!loading && !error) {
34 | order.itemsPrice = order.orderItems.reduce((acc, item) => acc + item.price * item.qty, 0).toFixed(2)
35 | }
36 |
37 |
38 | const addPayPalScript = () => {
39 | const script = document.createElement('script')
40 | script.type = 'text/javascript'
41 | script.src = 'https://www.paypal.com/sdk/js?client-id=AXDPk8kNaSQ5dEYV8cPgWYQAPg2uk8eCFwEXEzPaF0SlqSNDcKKvKzZxWUVwTwTSD8WKhXFXZ3XVZq69'
42 | script.async = true
43 | script.onload = () => {
44 | setSdkReady(true)
45 | }
46 | document.body.appendChild(script)
47 | }
48 |
49 | useEffect(() => {
50 |
51 | if (!userInfo) {
52 | history.push('/login_client')
53 | }
54 |
55 | if (!order || successPay || order._id !== Number(orderId) || successDeliver) {
56 | dispatch({ type: ORDER_PAY_RESET })
57 | dispatch({ type: ORDER_DELIVER_RESET })
58 |
59 | dispatch(getOrderDetails(orderId))
60 | } else if (!order.isPaid) {
61 | if (!window.paypal) {
62 | addPayPalScript()
63 | } else {
64 | setSdkReady(true)
65 | }
66 | }
67 | }, [dispatch, order, orderId, successPay, history, userInfo, successDeliver])
68 |
69 |
70 | const successPaymentHandler = (paymentResult) => {
71 | dispatch(payOrder(orderId, paymentResult))
72 | }
73 |
74 | const deliverHandler = () => {
75 | dispatch(deliverOrder(order))
76 | }
77 |
78 | return loading ? (
79 |
80 | ) : error ? (
81 | {error}
82 | ) : (
83 |
84 |
Order: {order.Id}
85 | {userInfo && !userInfo.isAdmin &&(
86 |
87 | Go Back
88 |
89 | )}
90 |
91 | {userInfo && userInfo.isAdmin && (
92 |
93 | Go Back To OrderList
94 |
95 | )}
96 |
97 |
98 |
99 |
100 |
101 | Shipping
102 | Name: {order.user.name}
103 | Email: {order.user.email}
104 |
105 | Shipping:
106 | {order.shippingAddress.address}, {order.shippingAddress.city}
107 | {' '}
108 | {order.shippingAddress.postalCode},
109 | {' '}
110 | {order.shippingAddress.country}
111 |
112 |
113 | {order.isDelivered ? (
114 | Your order has been delivered on{' '} {moment(order.deliveredAt).format('MMMM Do, YYYY')}
115 | ) : (
116 | Your order hasn't been delivered
117 | )}
118 |
119 |
120 |
121 |
122 | Payment Method
123 |
124 | Method:
125 | {order.paymentMethod}
126 |
127 | {order.isPaid ? (
128 | Your order has been paid on{' '} {moment(order.paidAt).format('MMMM Do, YYYY')}
129 | ) : (
130 | Not Paid
131 | )}
132 |
133 |
134 |
135 | Order Items
136 | {order.orderItems.length === 0 ?
137 | Order is empty
138 | : (
139 |
140 | {order.orderItems.map((item, index) => (
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 | {item.name}
149 |
150 |
151 |
152 | {item.qty} X €{item.price} = €{(item.qty * item.price).toFixed(2)}
153 |
154 |
155 |
156 | ))}
157 |
158 | )}
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 | Order Summary
170 |
171 |
172 |
173 |
174 | Items:
175 | €{order.itemsPrice}
176 |
177 |
178 |
179 |
180 |
181 | Shipping:
182 | €{order.shippingPrice}
183 |
184 |
185 |
186 |
187 |
188 | Tax:
189 | €{order.taxPrice}
190 |
191 |
192 |
193 |
194 |
195 | Total:
196 | €{order.totalPrice}
197 |
198 |
199 |
200 |
201 | {!order.isPaid && (
202 |
203 | {loadingPay && }
204 |
205 | {!sdkReady ? (
206 |
207 | ) : (
208 |
212 | )}
213 |
214 | )}
215 |
216 | {loadingDeliver && }
217 | {userInfo && userInfo.isAdmin && order.isPaid && !order.isDelivered && (
218 |
219 |
226 |
227 | )}
228 |
229 |
230 |
231 |
232 | )
233 | }
234 |
235 | export default OrderScreen
--------------------------------------------------------------------------------
/src/screens/PaymentScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { Form, Button, Col } from 'react-bootstrap'
3 | import { useDispatch, useSelector } from 'react-redux'
4 | import FormContainer from '../components/FormContainer'
5 | import CheckoutSteps from '../components/CheckoutSteps'
6 | import { savePaymentMethod } from '../actions/cartActions'
7 |
8 | function PaymentScreen({ history }) {
9 |
10 | const cart = useSelector(state => state.cart)
11 | const { shippingAddress } = cart
12 |
13 | const dispatch = useDispatch()
14 |
15 | const [paymentMethod, setPaymentMethod] = useState('PayPal')
16 |
17 | if (!shippingAddress.address) {
18 | history.push('/shipping')
19 | }
20 |
21 | const submitHandler = (e) => {
22 | e.preventDefault()
23 | dispatch(savePaymentMethod(paymentMethod))
24 | history.push('/placeorder')
25 | }
26 |
27 | return (
28 |
29 |
30 |
31 |
33 | Select Method
34 |
35 | setPaymentMethod(e.target.value)}
42 | >
43 |
44 |
45 |
46 |
47 |
48 |
51 |
52 |
53 | )
54 | }
55 |
56 | export default PaymentScreen
57 |
--------------------------------------------------------------------------------
/src/screens/PlaceOrderScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { Button, Row, Col, ListGroup, Image, Card } from 'react-bootstrap'
3 | import { Link } from 'react-router-dom'
4 | import { useDispatch, useSelector } from 'react-redux'
5 | import Message from '../components/Message'
6 | import CheckoutSteps from '../components/CheckoutSteps'
7 | import { createOrder } from '../actions/orderActions'
8 | import { ORDER_CREATE_RESET } from '../constants/orderConstants'
9 |
10 | function PlaceOrderScreen({ history }) {
11 |
12 | const orderCreate = useSelector(state => state.orderCreate)
13 | const { order, error, success } = orderCreate
14 |
15 | const dispatch = useDispatch()
16 |
17 | const cart = useSelector(state => state.cart)
18 |
19 | cart.itemsPrice = cart.cartItems.reduce((acc, item) => acc + item.price * item.qty, 0).toFixed(2)
20 | cart.shippingPrice = (cart.itemsPrice > 100 ? 0 : 10).toFixed(2)
21 | cart.taxPrice = Number((0.082) * cart.itemsPrice).toFixed(2)
22 |
23 | cart.totalPrice = (Number(cart.itemsPrice) + Number(cart.shippingPrice) + Number(cart.taxPrice)).toFixed(2)
24 |
25 |
26 | if (!cart.paymentMethod) {
27 | history.push('/payment')
28 | }
29 |
30 | useEffect(() => {
31 | if (success) {
32 | history.push(`/order/${order._id}`)
33 | dispatch({ type: ORDER_CREATE_RESET })
34 | }
35 | }, [dispatch, success, history])
36 |
37 | const placeOrder = () => {
38 | console.log('Place order')
39 | dispatch(createOrder({
40 |
41 | orderItems: cart.cartItems,
42 | shippingAddress: cart.shippingAddress,
43 | paymentMethod: cart.paymentMethod,
44 | itemsPrice: cart.itemsPrice,
45 | shippingPrice: cart.shippingPrice,
46 | taxPrice: cart.taxPrice,
47 | totalPrice: cart.totalPrice,
48 | }))
49 | }
50 |
51 | return (
52 |
53 |
54 |
55 |
56 |
57 |
58 | Shipping
59 |
60 |
61 | Shipping:
62 | {cart.shippingAddress.address}, {cart.shippingAddress.city}
63 | {' '}
64 | {cart.shippingAddress.postalCode},
65 | {' '}
66 | {cart.shippingAddress.country}
67 |
68 |
69 |
70 |
71 | Payment Method
72 |
73 | Method:
74 | {cart.paymentMethod}
75 |
76 |
77 |
78 |
79 | Order Items
80 | {cart.cartItems.length === 0 ?
81 | Your cart is empty
82 | : (
83 |
84 | {cart.cartItems.map((item, index) => (
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | {item.name}
93 |
94 |
95 |
96 | {item.qty} X ${item.price} = ${(item.qty * item.price).toFixed(2)}
97 |
98 |
99 |
100 | ))}
101 |
102 | )}
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | Order Summary
114 |
115 |
116 |
117 |
118 | Items:
119 | ${cart.itemsPrice}
120 |
121 |
122 |
123 |
124 |
125 | Shipping:
126 | ${cart.shippingPrice}
127 |
128 |
129 |
130 |
131 |
132 | Tax:
133 | ${cart.taxPrice}
134 |
135 |
136 |
137 |
138 |
139 | Total:
140 | ${cart.totalPrice}
141 |
142 |
143 |
144 |
145 |
146 | {error && {error}}
147 |
148 |
149 |
150 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | )
166 | }
167 |
168 | export default PlaceOrderScreen
169 |
--------------------------------------------------------------------------------
/src/screens/ProductEditScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import axios from 'axios'
3 | import { Link } from 'react-router-dom'
4 | import { Form, Button } from 'react-bootstrap'
5 | import { useDispatch, useSelector } from 'react-redux'
6 | import Loader from '../components/Loader'
7 | import Message from '../components/Message'
8 | import FormContainer from '../components/FormContainer'
9 | import { listProductDetails, updateProduct } from '../actions/productActions'
10 | import { PRODUCT_UPDATE_RESET } from '../constants/productConstants'
11 |
12 |
13 | function ProductEditScreen({ match, history }) {
14 |
15 | const productId = match.params.id
16 |
17 | const [name, setName] = useState('')
18 | const [price, setPrice] = useState(0)
19 | const [image, setImage] = useState('')
20 | const [brand, setBrand] = useState('')
21 | const [category, setCategory] = useState('')
22 | const [countInStock, setCountInStock] = useState(0)
23 | const [description, setDescription] = useState('')
24 | const [uploading, setUploading] = useState(false)
25 |
26 | const dispatch = useDispatch()
27 |
28 | const productDetails = useSelector(state => state.productDetails)
29 | const { error, loading, product } = productDetails
30 |
31 | const productUpdate = useSelector(state => state.productUpdate)
32 | const { error: errorUpdate, loading: loadingUpdate, success: successUpdate } = productUpdate
33 |
34 |
35 | useEffect(() => {
36 |
37 | if (successUpdate) {
38 | dispatch({ type: PRODUCT_UPDATE_RESET })
39 | history.push('/admin/productlist')
40 | } else {
41 | if (!product.name || product._id !== Number(productId)) {
42 | dispatch(listProductDetails(productId))
43 | } else {
44 | setName(product.name)
45 | setPrice(product.price)
46 | setImage(product.image)
47 | setBrand(product.brand)
48 | setCategory(product.category)
49 | setCountInStock(product.countInStock)
50 | setDescription(product.description)
51 |
52 | }
53 | }
54 |
55 |
56 |
57 | }, [dispatch, product, productId, history, successUpdate])
58 |
59 | const submitHandler = (e) => {
60 | e.preventDefault()
61 | dispatch(updateProduct({
62 | _id: productId,
63 | name,
64 | price,
65 | image,
66 | brand,
67 | category,
68 | countInStock,
69 | description
70 | }))
71 | }
72 |
73 | const uploadFileHandler = async (e) => {
74 | const file = e.target.files[0]
75 | const formData = new FormData()
76 |
77 | formData.append('image', file)
78 | formData.append('product_id', productId)
79 |
80 | setUploading(true)
81 |
82 | try {
83 | const config = {
84 | headers: {
85 | 'Content-Type': 'multipart/form-data'
86 | }
87 | }
88 |
89 | const { data } = await axios.post('/api/products/upload/', formData, config)
90 |
91 |
92 | setImage(data)
93 | setUploading(false)
94 |
95 | } catch (error) {
96 | setUploading(false)
97 | }
98 | }
99 |
100 |
101 | return (
102 |
103 |
104 | Go Back To ProductsList
105 |
106 |
107 |
108 | Edit Product
109 | {loadingUpdate && }
110 | {errorUpdate && {errorUpdate}}
111 |
112 | {loading ? : error ? {error}
113 | : (
114 |
117 | Name
118 | setName(e.target.value)}
124 | >
125 |
126 |
127 |
128 |
129 | Price
130 | setPrice(e.target.value)}
136 | >
137 |
138 |
139 |
140 |
141 |
142 | Image
143 | setImage(e.target.value)}
149 | >
150 |
151 |
152 |
158 |
159 |
160 | {uploading && }
161 |
162 |
163 |
164 |
165 |
166 | Brand
167 | setBrand(e.target.value)}
173 | >
174 |
175 |
176 |
177 |
178 | Stock
179 | setCountInStock(e.target.value)}
185 | >
186 |
187 |
188 |
189 |
190 | Category
191 | setCategory(e.target.value)}
197 | >
198 |
199 |
200 |
201 |
202 | Description
203 | setDescription(e.target.value)}
209 | >
210 |
211 |
212 |
213 |
214 |
217 |
218 |
219 | )}
220 |
221 |
222 |
223 |
224 | )
225 | }
226 |
227 | export default ProductEditScreen
--------------------------------------------------------------------------------
/src/screens/ProductListScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { LinkContainer } from 'react-router-bootstrap'
3 | import { Table, Button, Row, Col } from 'react-bootstrap'
4 | import { useDispatch, useSelector } from 'react-redux'
5 | import Loader from '../components/Loader'
6 | import Message from '../components/Message'
7 | import Paginate from '../components/Paginate'
8 | import { listProducts, deleteProduct, createProduct } from '../actions/productActions'
9 | import { PRODUCT_CREATE_RESET } from '../constants/productConstants'
10 |
11 | function ProductListScreen({ history, match }) {
12 |
13 | const dispatch = useDispatch()
14 |
15 | const productList = useSelector(state => state.productList)
16 | const { loading, error, products, page, pages } = productList
17 |
18 | const productDelete = useSelector(state => state.productDelete)
19 | const { loading: loadingDelete, error: errorDelete, success: successDelete } = productDelete
20 |
21 | const productCreate = useSelector(state => state.productCreate)
22 | const { loading: loadingCreate, error: errorCreate, success: successCreate, product: createdProduct } = productCreate
23 |
24 |
25 | const userLogin = useSelector(state => state.userLogin)
26 | const { userInfo } = userLogin
27 |
28 | let keyword = history.location.search
29 |
30 | useEffect(() => {
31 | dispatch({ type: PRODUCT_CREATE_RESET })
32 | if (!userInfo.isAdmin) {
33 | history.push('/login_client')
34 | }
35 |
36 | if (successCreate) {
37 | history.push(`/admin/product/${createdProduct._id}/edit`)
38 | } else {
39 | dispatch(listProducts(keyword))
40 | }
41 |
42 | }, [dispatch, history, userInfo, successDelete, successCreate, createdProduct, keyword])
43 |
44 |
45 | const deleteHandler = (id) => {
46 |
47 | if (window.confirm('Are you sure you want to delete this product?')) {
48 | dispatch(deleteProduct(id))
49 | }
50 | }
51 |
52 | const createProductHandler = () => {
53 | dispatch(createProduct())
54 | }
55 |
56 | return (
57 |
58 |
59 |
60 | Products
61 |
62 |
63 |
64 |
67 |
68 |
69 |
70 | {loadingDelete &&
}
71 | {errorDelete &&
{errorDelete}}
72 |
73 |
74 | {loadingCreate &&
}
75 | {errorCreate &&
{errorCreate}}
76 |
77 | {loading
78 | ? (
)
79 | : error
80 | ? (
{error})
81 | : (
82 |
83 |
84 |
85 |
86 | ID |
87 | NAME |
88 | PRICE |
89 | CATEGORY |
90 | BRAND |
91 | |
92 |
93 |
94 |
95 |
96 | {products.map(product => (
97 |
98 | {product._id} |
99 | {product.name} |
100 | €{product.price} |
101 | {product.category} |
102 | {product.brand} |
103 |
104 |
105 |
106 |
109 |
110 |
111 |
114 | |
115 |
116 | ))}
117 |
118 |
119 |
120 |
121 | )}
122 |
123 | )
124 | }
125 |
126 | export default ProductListScreen
--------------------------------------------------------------------------------
/src/screens/ProductScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { useDispatch, useSelector } from 'react-redux'
3 | import { Link } from 'react-router-dom'
4 | import { Row, Col, Image, ListGroup, Button, Card, Form } from 'react-bootstrap'
5 | import Rating from '../components/Rating'
6 | import Loader from '../components/Loader'
7 | import Message from '../components/Message'
8 | /*import axios from 'axios'*/
9 | import { listProductDetails, createProductReview } from '../actions/productActions'
10 | //import { PRODUCT_CREATE_REVIEW_RESET } from '../constants/productConstants'
11 |
12 |
13 | function ProductScreen({ match, history }) {
14 | const [qty, setQty] = useState(1)
15 | const [rating, setRating] = useState(0)
16 | const [comment, setComment] = useState('')
17 |
18 | const dispatch = useDispatch()
19 |
20 | const productDetails = useSelector(state => state.productDetails)
21 | const { loading, error, product } = productDetails
22 |
23 | const userLogin = useSelector(state => state.userLogin)
24 | const { userInfo } = userLogin
25 |
26 | const productReviewCreate = useSelector(state => state.productReviewCreate)
27 | const {
28 | loading: loadingProductReview,
29 | error: errorProductReview,
30 | success: successProductReview,
31 | } = productReviewCreate
32 |
33 | useEffect(() => {
34 | dispatch(listProductDetails(match.params.id))
35 | }, [dispatch, match])
36 |
37 | const addToCartHandler = () => {
38 | history.push(`/cart/${match.params.id}?qty=${qty}`)
39 | }
40 |
41 | const submitHandler = (e) => {
42 | e.preventDefault()
43 | dispatch(createProductReview(
44 | match.params.id, {
45 | rating,
46 | comment
47 | }
48 | ))
49 | }
50 |
51 | return (
52 |
53 |
54 | Go Back To Products
55 |
56 | {loading ?
57 |
58 | : error
59 | ?
{error}
60 | : (
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | {product.name}
71 |
72 |
73 |
74 |
75 | {`${product.numReviews} reviews`}
76 |
77 |
78 |
79 | Price: €{product.price}
80 |
81 |
82 |
83 | Description: {product.description}
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | Price:
94 |
95 | €{product.price}
96 |
97 |
98 |
99 |
100 |
101 |
102 | Status:
103 |
104 | {product.countInStock > 0 ? 'In Stock' : 'Out of Stock'}
105 |
106 |
107 |
108 |
109 | {product.countInStock > 0 && (
110 |
111 |
112 | Quantity
113 |
114 | setQty(e.target.value)}
118 | >
119 | {
120 | [...Array(product.countInStock).keys()].map((x) => (
121 |
124 |
125 | ))
126 | }
127 |
128 |
129 |
130 |
131 |
132 | )}
133 |
134 |
135 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | Reviews
150 | {product.reviews.length === 0 && No Reviews}
151 |
152 |
153 | {product.reviews.map((review) => (
154 |
155 | {review.name}
156 |
157 | {review.createdAt.substring(0, 10)}
158 | {review.comment}
159 |
160 | ))}
161 |
162 |
163 | Write a review
164 |
165 | {loadingProductReview && }
166 | {successProductReview && Review Submitted}
167 | {errorProductReview && {errorProductReview}}
168 |
169 | {userInfo ? (
170 |
172 | Rating
173 | setRating(e.target.value)}
177 | >
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 | Review
189 | setComment(e.target.value)}
194 | >
195 |
196 |
197 |
204 |
205 |
206 | ) : (
207 | Please login to write a review
208 | )}
209 |
210 |
211 |
212 |
213 |
214 | )
215 | }
216 |
217 |
218 | )
219 | }
220 |
221 | export default ProductScreen
222 |
--------------------------------------------------------------------------------
/src/screens/ProfileScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | //import { Link } from 'react-router-dom'
3 | import { Form, Button, Row, Col, Table } from 'react-bootstrap'
4 | import { LinkContainer } from 'react-router-bootstrap'
5 | import { useDispatch, useSelector } from 'react-redux'
6 | import Loader from '../components/Loader'
7 | import Message from '../components/Message'
8 | import { getUserDetails, updateUserProfile } from '../actions/userActions'
9 | import { USER_UPDATE_PROFILE_RESET } from '../constants/userConstants'
10 | import { listMyOrders } from '../actions/orderActions'
11 |
12 | function ProfileScreen({ history }) {
13 |
14 | const [name, setName] = useState('')
15 | const [email, setEmail] = useState('')
16 | const [password, setPassword] = useState('')
17 | const [confirmPassword, setConfirmPassword] = useState('')
18 | const [message, setMessage] = useState('')
19 |
20 | const dispatch = useDispatch()
21 |
22 | const userDetails = useSelector(state => state.userDetails)
23 | const { error, loading, user } = userDetails
24 |
25 | const userLogin = useSelector(state => state.userLogin)
26 | const { userInfo } = userLogin
27 |
28 | const userUpdateProfile = useSelector(state => state.userUpdateProfile)
29 | const { success } = userUpdateProfile
30 |
31 | const orderListMy = useSelector(state => state.orderListMy)
32 | const { loading: loadingOrders, error: errorOrders, orders } = orderListMy
33 |
34 | useEffect(() => {
35 | if (!userInfo) {
36 | history.push('/login_client')
37 | } else {
38 | if (!user || !user.name || success || userInfo._id !== user._id) {
39 | dispatch({ type: USER_UPDATE_PROFILE_RESET })
40 | dispatch(getUserDetails('profile'))
41 | dispatch(listMyOrders())
42 | } else {
43 | setName(user.name)
44 | setEmail(user.email)
45 | }
46 | }
47 | }, [dispatch, history, userInfo, user, success])
48 |
49 | const submitHandler = (e) => {
50 | e.preventDefault()
51 |
52 | if (password !== confirmPassword) { // change on 11.04 !=
53 | setMessage('Passwords do not match')
54 | } else {
55 | dispatch(updateUserProfile({
56 | 'id': user._id,
57 | 'name': name,
58 | 'email': email,
59 | 'password': password
60 | }))
61 | setMessage('')
62 | }
63 |
64 | }
65 | return (
66 |
67 |
68 | User Profile
69 |
70 | {message && {message}}
71 | {error && {error}}
72 | {loading && }
73 |
76 | Name
77 | setName(e.target.value)}
83 | >
84 |
85 |
86 |
87 |
88 | Email Address
89 | setEmail(e.target.value)}
95 | >
96 |
97 |
98 |
99 |
100 | Password
101 | setPassword(e.target.value)}
107 | >
108 |
109 |
110 |
111 |
112 | Confirm Password
113 | setConfirmPassword(e.target.value)}
119 | >
120 |
121 |
122 |
123 |
126 |
127 |
128 |
129 |
130 |
131 | My Orders
132 | {loadingOrders ? (
133 |
134 | ) : errorOrders ? (
135 | {errorOrders}
136 | ) : (
137 |
138 |
139 |
140 | ID |
141 | Date |
142 | Total |
143 | Paid |
144 | Delivered |
145 | |
146 |
147 |
148 |
149 |
150 | {orders.map(order => (
151 |
152 | {order._id} |
153 | {order.createdAt.substring(0, 10)} |
154 | €{order.totalPrice} |
155 | {order.isPaid ? order.paidAt.substring(0, 10) : (
156 |
157 | )} |
158 |
159 |
160 |
161 |
162 | |
163 |
164 | ))}
165 |
166 |
167 | )}
168 |
169 |
170 |
171 | )
172 | }
173 |
174 | export default ProfileScreen
--------------------------------------------------------------------------------
/src/screens/RegisterScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { Form, Button, Row, Col } from 'react-bootstrap'
4 | import { useDispatch, useSelector } from 'react-redux'
5 | import Loader from '../components/Loader'
6 | import Message from '../components/Message'
7 | import FormContainer from '../components/FormContainer'
8 | import { register } from '../actions/userActions'
9 |
10 | function RegisterScreen({ location, history }) {
11 |
12 | const [name, setName] = useState('')
13 | const [email, setEmail] = useState('')
14 | const [password, setPassword] = useState('')
15 | const [confirmPassword, setConfirmPassword] = useState('')
16 | const [message, setMessage] = useState('')
17 |
18 | const dispatch = useDispatch()
19 |
20 | const redirect = location.search ? location.search.split('=')[1] : '/'
21 |
22 | const userRegister = useSelector(state => state.userRegister)
23 | const { error, loading, userInfo } = userRegister
24 |
25 | useEffect(() => {
26 | if (userInfo) {
27 | history.push(redirect)
28 | }
29 | }, [history, userInfo, redirect])
30 |
31 | const submitHandler = (e) => {
32 | e.preventDefault()
33 |
34 | if (password !== confirmPassword) { // modified on 11.04 !=
35 | setMessage('Passwords do not match')
36 | } else {
37 | dispatch(register(name, email, password))
38 | }
39 |
40 | }
41 |
42 | return (
43 |
44 | Sign In
45 | {message && {message}}
46 | {error && {error}}
47 | {loading && }
48 |
51 | Name
52 | setName(e.target.value)}
58 | >
59 |
60 |
61 |
62 |
63 | Email Address
64 | setEmail(e.target.value)}
70 | >
71 |
72 |
73 |
74 |
75 | Password
76 | setPassword(e.target.value)}
82 | >
83 |
84 |
85 |
86 |
87 | Confirm Password
88 | setConfirmPassword(e.target.value)}
94 | >
95 |
96 |
97 |
98 |
101 |
102 |
103 |
104 |
105 |
106 | Have an Account?
108 | Sign In
109 |
110 |
111 |
112 |
113 | )
114 | }
115 |
116 | export default RegisterScreen
117 |
--------------------------------------------------------------------------------
/src/screens/ResultProductScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { useDispatch, useSelector } from 'react-redux'
3 | import { Row, Col } from 'react-bootstrap'
4 | import Product from '../components/Product'
5 | import Loader from '../components/Loader'
6 | import Message from '../components/Message'
7 | import Paginate from '../components/Paginate'
8 | import { listProducts } from '../actions/productActions'
9 | import SearchProduct from '../components/SearchProduct'
10 |
11 |
12 |
13 | function ResultProductScreen({history}) {
14 | const dispatch = useDispatch()
15 | const productList = useSelector(state => state.productList)
16 | const { error, loading, products, page, pages } = productList
17 |
18 | let keyword = history.location.search
19 |
20 | useEffect(() => {
21 | dispatch(listProducts(keyword))
22 |
23 | }, [dispatch, keyword])
24 |
25 | return (
26 |
27 |
28 |
29 |
30 |
Results of your search:
31 | {loading ?
32 | : error ?
{error}
33 | :
34 |
35 |
36 | {products.map(product => (
37 |
38 |
39 |
40 | ))}
41 |
42 |
43 |
44 | }
45 |
46 |
47 | )
48 | }
49 |
50 | export default ResultProductScreen
51 |
--------------------------------------------------------------------------------
/src/screens/ResultShopScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { useDispatch, useSelector } from 'react-redux'
3 | import { Row, Col } from 'react-bootstrap'
4 | import Shop from '../components/Shop'
5 | import Loader from '../components/Loader'
6 | import Message from '../components/Message'
7 | import PaginateShop from '../components/PaginateShop'
8 | import { listShops } from '../actions/shopActions'
9 | import SearchShop from '../components/SearchShop'
10 |
11 |
12 |
13 | function ResultShopScreen(history) {
14 | const dispatch = useDispatch()
15 | const shopList = useSelector(state => state.shopList)
16 | const { error, loading, shops, page, pages } = shopList
17 |
18 | let keyword = history.location.search
19 |
20 | useEffect(() => {
21 | dispatch(listShops(keyword))
22 |
23 | }, [dispatch, keyword])
24 |
25 | return (
26 |
27 |
28 |
29 |
30 |
Results of your search:
31 | {loading ?
32 | : error ?
{error}
33 | :
34 |
35 |
36 | {shops.map(shop => (
37 |
38 |
39 |
40 | ))}
41 |
42 |
43 |
44 | }
45 |
46 |
47 | )
48 | }
49 |
50 | export default ResultShopScreen
51 |
--------------------------------------------------------------------------------
/src/screens/ShippingScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { Form, Button } from 'react-bootstrap'
3 | import { useDispatch, useSelector } from 'react-redux'
4 | import FormContainer from '../components/FormContainer'
5 | import CheckoutSteps from '../components/CheckoutSteps'
6 | import { saveShippingAddress } from '../actions/cartActions'
7 |
8 | function ShippingScreen({ history }) {
9 |
10 | const cart = useSelector(state => state.cart)
11 | const { shippingAddress } = cart
12 |
13 | const dispatch = useDispatch()
14 |
15 | const [address, setAddress] = useState(shippingAddress.address)
16 | const [city, setCity] = useState(shippingAddress.city)
17 | const [postalCode, setPostalCode] = useState(shippingAddress.postalCode)
18 | const [country, setCountry] = useState(shippingAddress.country)
19 |
20 | const submitHandler = (e) => {
21 | e.preventDefault()
22 | dispatch(saveShippingAddress({ address, city, postalCode, country }))
23 | history.push('/payment')
24 | }
25 |
26 | return (
27 |
28 |
29 | Shipping
30 |
33 | Address
34 | setAddress(e.target.value)}
40 | >
41 |
42 |
43 |
44 |
45 | City
46 | setCity(e.target.value)}
52 | >
53 |
54 |
55 |
56 |
57 | Postal Code
58 | setPostalCode(e.target.value)}
64 | >
65 |
66 |
67 |
68 |
69 | Country
70 | setCountry(e.target.value)}
76 | >
77 |
78 |
79 |
80 |
83 |
84 |
85 | )
86 | }
87 |
88 | export default ShippingScreen
89 |
--------------------------------------------------------------------------------
/src/screens/ShopScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { useDispatch, useSelector } from 'react-redux'
3 | import { Link } from 'react-router-dom'
4 | import { Row, Col, ListGroup, Button, Card, Form } from 'react-bootstrap'
5 | import Rating from '../components/Rating'
6 | import Loader from '../components/Loader'
7 | import Message from '../components/Message'
8 | //import axios from 'axios'
9 | import { listShopDetails, createShopReview } from '../actions/shopActions'
10 | //import { SHOP_CREATE_REVIEW_RESET } from '../constants/shopConstants'
11 |
12 |
13 | function ShopScreen({ match }) {
14 | const [rating, setRating] = useState(0)
15 | const [comment, setComment] = useState('')
16 |
17 | const dispatch = useDispatch()
18 |
19 | const shopDetails = useSelector(state => state.shopDetails)
20 | const { loading, error, shop } = shopDetails
21 |
22 | const userLogin = useSelector(state => state.userLogin)
23 | const { userInfo } = userLogin
24 |
25 | const shopReviewCreate = useSelector(state => state.shopReviewCreate)
26 | const {
27 | loading: loadingShopReview,
28 | error: errorShopReview,
29 | success: successShopReview,
30 | } = shopReviewCreate
31 |
32 | useEffect(() => {
33 | dispatch(listShopDetails(match.params.id))
34 | }, [dispatch, match])
35 |
36 |
37 | const submitHandler = (e) => {
38 | e.preventDefault()
39 | dispatch(createShopReview(
40 | match.params.id, {
41 | rating,
42 | comment
43 | }
44 | ))
45 | }
46 |
47 | return (
48 |
49 |
50 | Go Back To Shops
51 |
52 | {loading ?
53 |
54 | : error
55 | ?
{error}
56 | : (
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
72 |
73 |
79 |
80 | {/* */}
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | {shop.name}
90 | {shop.address}
91 | {shop.postalCode} {shop.city}
92 | {shop.state}
93 |
94 |
95 |
96 | {shop.phone}
97 | {shop.hours}
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | {shop.description}
106 |
107 |
108 |
109 |
110 |
111 |
112 | Reviews
113 | {shop.reviews.length === 0 && No Reviews}
114 |
115 |
116 | {shop.reviews.map((review) => (
117 |
118 | {review.name}
119 |
120 | {review.createdAt.substring(0, 10)}
121 | {review.comment}
122 |
123 | ))}
124 |
125 |
126 | Write a review
127 |
128 | {loadingShopReview && }
129 | {successShopReview && Review Submitted}
130 | {errorShopReview && {errorShopReview}}
131 |
132 | {userInfo ? (
133 |
135 | Rating
136 | setRating(e.target.value)}
140 | >
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 | Review
152 | setComment(e.target.value)}
157 | >
158 |
159 |
160 |
167 |
168 |
169 | ) : (
170 | Please login to write a review
171 | )}
172 |
173 |
174 |
175 |
176 |
177 | )
178 | }
179 |
180 |
181 | )
182 | }
183 |
184 | export default ShopScreen
--------------------------------------------------------------------------------
/src/screens/UserEditScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { Link } from 'react-router-dom'
3 | import { Form, Button } from 'react-bootstrap'
4 | import { useDispatch, useSelector } from 'react-redux'
5 | import Loader from '../components/Loader'
6 | import Message from '../components/Message'
7 | import FormContainer from '../components/FormContainer'
8 | import { getUserDetails, updateUser } from '../actions/userActions'
9 | import { USER_UPDATE_RESET } from '../constants/userConstants'
10 |
11 | function UserEditScreen({ match, history }) {
12 |
13 | const userId = match.params.id
14 |
15 | const [name, setName] = useState('')
16 | const [email, setEmail] = useState('')
17 | const [isAdmin, setIsAdmin] = useState(false)
18 |
19 | const dispatch = useDispatch()
20 |
21 | const userDetails = useSelector(state => state.userDetails)
22 | const { error, loading, user } = userDetails
23 |
24 | const userUpdate = useSelector(state => state.userUpdate)
25 | const { error: errorUpdate, loading: loadingUpdate, success: successUpdate } = userUpdate
26 |
27 | useEffect(() => {
28 |
29 | if (successUpdate) {
30 | dispatch({ type: USER_UPDATE_RESET })
31 | history.push('/admin/userlist')
32 | } else {
33 |
34 | if (!user.name || user._id !== Number(userId)) {
35 | dispatch(getUserDetails(userId))
36 | } else {
37 | setName(user.name)
38 | setEmail(user.email)
39 | setIsAdmin(user.isAdmin)
40 | }
41 | }
42 |
43 | }, [dispatch, user, userId, successUpdate, history])
44 |
45 | const submitHandler = (e) => {
46 | e.preventDefault()
47 | dispatch(updateUser({ _id: user._id, name, email, isAdmin }))
48 | }
49 |
50 | return (
51 |
52 |
53 | Go Back To UsersList
54 |
55 |
56 |
57 | Edit User
58 | {loadingUpdate && }
59 | {errorUpdate && {errorUpdate}}
60 |
61 | {loading ? : error ? {error}
62 | : (
63 |
66 | Name
67 | setName(e.target.value)}
73 | >
74 |
75 |
76 |
77 |
78 | Email Address
79 | setEmail(e.target.value)}
84 | >
85 |
86 |
87 |
88 |
89 |
95 |
96 |
97 |
98 |
101 |
102 |
103 | )}
104 |
105 |
106 |
107 |
108 | )
109 | }
110 |
111 | export default UserEditScreen
--------------------------------------------------------------------------------
/src/screens/UserListScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { LinkContainer } from 'react-router-bootstrap'
3 | import { Table, Button } from 'react-bootstrap'
4 | import { useDispatch, useSelector } from 'react-redux'
5 | import Loader from '../components/Loader'
6 | import Message from '../components/Message'
7 | import { listUsers, deleteUser } from '../actions/userActions'
8 |
9 | function UserListScreen({ history }) {
10 |
11 | const dispatch = useDispatch()
12 |
13 | const userList = useSelector(state => state.userList)
14 | const { loading, error, users } = userList
15 |
16 | const userLogin = useSelector(state => state.userLogin)
17 | const { userInfo } = userLogin
18 |
19 | const userDelete = useSelector(state => state.userDelete)
20 | const { success: successDelete } = userDelete
21 |
22 |
23 | useEffect(() => {
24 | if (userInfo && userInfo.isAdmin) {
25 | dispatch(listUsers())
26 | } else {
27 | history.push('/login_client')
28 | }
29 |
30 | }, [dispatch, history, successDelete, userInfo])
31 |
32 |
33 | const deleteHandler = (id) => {
34 |
35 | if (window.confirm('Are you sure you want to delete this user?')) {
36 | dispatch(deleteUser(id))
37 | }
38 | }
39 |
40 | return (
41 |
42 |
Users
43 | {loading
44 | ? (
)
45 | : error
46 | ? (
{error})
47 | : (
48 |
49 |
50 |
51 | ID |
52 | NAME |
53 | EMAIL |
54 | ADMIN |
55 | |
56 |
57 |
58 |
59 |
60 | {users.map(user => (
61 |
62 | {user._id} |
63 | {user.name} |
64 | {user.email} |
65 | {user.isAdmin ? (
66 |
67 | ) : (
68 |
69 | )} |
70 |
71 |
72 |
73 |
76 |
77 |
78 |
81 | |
82 |
83 | ))}
84 |
85 |
86 | )}
87 |
88 | )
89 | }
90 |
91 | export default UserListScreen
92 |
--------------------------------------------------------------------------------
/src/shops.js:
--------------------------------------------------------------------------------
1 | const shops = [
2 | {
3 | '_id': '1',
4 | 'name': 'Afro Shop Iman',
5 | 'image': '/images/bg_1.jpg',
6 | 'address': 'Vorstadtstr. 33',
7 | 'city': 'Saarbrücken',
8 | 'state': 'Saarland',
9 | 'postalCode': '66117',
10 | 'country': 'Deutschland',
11 | 'category': 'AfroShop',
12 | 'rating': 4.5,
13 | 'numReviews': 1,
14 | 'description':'No description for now - Coming soon!',
15 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
16 | 'phone': '0681-9703917',
17 | },
18 | {
19 | '_id': '2',
20 | 'name': 'Afro Shop Dafrob',
21 | 'image': '/images/bg_2.jpg',
22 | 'address': 'Hüttenstr. 5',
23 | 'city': 'Neuenkirchen',
24 | 'state': 'Saarland',
25 | 'postalCode': '66538',
26 | 'country': 'Deutschland',
27 | 'category': 'AfroShop',
28 | 'rating': 4.0,
29 | 'numReviews': 2,
30 | 'description':'No description for now - Coming soon!',
31 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
32 | 'phone': '017670787794',
33 | },
34 | {
35 | '_id': '3',
36 | 'name': 'Bobby Afro Shop',
37 | 'image': '/images/bg_3.jpg',
38 | 'address': 'Bergstraße 61',
39 | 'city': 'Saarbrücken',
40 | 'state': 'Saarland',
41 | 'postalCode': '66115 ',
42 | 'country': 'Deutschland',
43 | 'category': 'AfroShop',
44 | 'rating': 3.0,
45 | 'numReviews': 5,
46 | 'description':'No description for now - Coming soon!',
47 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
48 | 'phone': '0681-9977239',
49 | },
50 | {
51 | '_id': '4',
52 | 'name': 'Gye Nyame Afro Shop',
53 | 'image': '/images/bg_4.png',
54 | 'address': 'Klausenerstraße 20',
55 | 'city': 'Saarbrücken',
56 | 'state': 'Saarland',
57 | 'postalCode': '66115 ',
58 | 'country': 'Deutschland',
59 | 'category': 'AfroShop',
60 | 'rating': 4.0,
61 | 'numReviews': 2,
62 | 'description':'No description for now - Coming soon!',
63 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
64 | 'phone': '0681-93556245',
65 | },
66 | {
67 | '_id': '5',
68 | 'name': 'Linas Afro Shop',
69 | 'image': '/images/bg_5.jpg',
70 | 'address': 'Johannisstr. 31',
71 | 'city': 'Saarbrücken ',
72 | 'state': 'Saarland',
73 | 'postalCode': '66111 ',
74 | 'country': 'Deutschland',
75 | 'category': 'AfroShop',
76 | 'rating': 2.0,
77 | 'numReviews': 2,
78 | 'description':'No description for now - Coming soon!',
79 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
80 | 'phone': 'Telefon nicht bekannt',
81 | },
82 | {
83 | '_id': '6',
84 | 'name': 'Afro Shop Tropicana',
85 | 'image': '/images/bg_6.png',
86 | 'address': 'Berliner Promenade 17',
87 | 'city': 'Saarbrücken',
88 | 'state': 'Saarland',
89 | 'postalCode': '66111 ',
90 | 'country': 'Deutschland',
91 | 'category': 'AfroShop',
92 | 'rating': 3.5,
93 | 'numReviews': 12,
94 | 'description':'No description for now - Coming soon!',
95 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
96 | 'phone': '0681-37996148',
97 | },
98 | {
99 | '_id': '7',
100 | 'name': 'Melanies AfroShop',
101 | 'image': '/images/bg_7.png',
102 | 'address': 'Karcherstraße 23',
103 | 'city': 'Saarlouis',
104 | 'state': 'Saarland',
105 | 'postalCode': '66740 ',
106 | 'country': 'Deutschland',
107 | 'category': 'AfroShop',
108 | 'rating': 4.0,
109 | 'numReviews': 2,
110 | 'description':'No description for now - Coming soon!',
111 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
112 | 'phone': '06831-8906992',
113 | },
114 | {
115 | '_id': '8',
116 | 'name': 'Alpha & Omega Int.',
117 | 'image': '/images/bg_8.png',
118 | 'address': 'Weserstr. 3-4 ',
119 | 'city': 'Berlin ',
120 | 'state': 'Berlin ',
121 | 'postalCode': '12047 ',
122 | 'country': 'Deutschland',
123 | 'category': 'AfroShop',
124 | 'rating': 4.0,
125 | 'numReviews': 2,
126 | 'description':'No description for now - Coming soon!',
127 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
128 | 'phone': '03061-624972',
129 | },
130 | {
131 | '_id': '9',
132 | 'name': 'Afro By Mary',
133 | 'image': '/images/bg_9.png',
134 | 'address': 'Zingster str 20',
135 | 'city': 'Berlin',
136 | 'state': 'Berlin',
137 | 'postalCode': '13051',
138 | 'country': 'Deutschland',
139 | 'category': 'AfroShop',
140 | 'rating': 1.0,
141 | 'numReviews': 1,
142 | 'description':'No description for now - Coming soon!',
143 | 'hours': 'Online',
144 | 'phone': '01734594121',
145 | },
146 | {
147 | '_id': '10',
148 | 'name': 'Afrika Vitrine',
149 | 'image': '/images/bg_10.png',
150 | 'address': 'Potsdamer Str. 156',
151 | 'city': 'Berlin–Schöneberg',
152 | 'state': 'Berlin',
153 | 'postalCode': '10783 ',
154 | 'country': 'Deutschland',
155 | 'category': 'AfroShop',
156 | 'rating': 2.0,
157 | 'numReviews': 2,
158 | 'description':'No description for now - Coming soon!',
159 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
160 | 'phone': 'Telefon nicht bekannt',
161 | },
162 | {
163 | '_id': '11',
164 | 'name': 'Asia & Afro Shop',
165 | 'image': '/images/bg_11.png',
166 | 'address': 'Leykestr. 1',
167 | 'city': 'Berlin–Neukölln',
168 | 'state': 'Berlin',
169 | 'postalCode': '12053 ',
170 | 'country': 'Deutschland',
171 | 'category': 'AfroShop',
172 | 'rating': 3.0,
173 | 'numReviews': 3,
174 | 'description':'No description for now - Coming soon!',
175 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
176 | 'phone': 'Telefon nicht bekannt',
177 | },
178 | {
179 | '_id': '12',
180 | 'name': 'Chriskes Afro Shop',
181 | 'image': '/images/bg_12.png',
182 | 'address': 'Rheinstraße 69',
183 | 'city': 'Wiesbaden',
184 | 'state': 'Hessen',
185 | 'postalCode': '65185',
186 | 'country': 'Deutschland',
187 | 'category': 'AfroShop',
188 | 'rating': 2.5,
189 | 'numReviews': 1,
190 | 'description':'No description for now - Coming soon!',
191 | 'hours': 'Mo - Sa : 09.00 - 20.00 Uhr',
192 | 'phone': '061118171740',
193 | },
194 | {
195 | '_id': '13',
196 | 'name': 'African Bazaar',
197 | 'image': '/images/bg_13.png',
198 | 'address': 'Mainzer Landstraße 107',
199 | 'city': 'Frankfurt am Main',
200 | 'state': 'Hessen',
201 | 'postalCode': '60329 ',
202 | 'country': 'Deutschland',
203 | 'category': 'AfroShop',
204 | 'rating': 4.5,
205 | 'numReviews': 1,
206 | 'description':'No description for now - Coming soon!',
207 | 'hours': 'Mo - Sa : 09.00 - 20.00 Uhr',
208 | 'phone': '069259488',
209 | },
210 | {
211 | '_id': '14',
212 | 'name': 'African Shop',
213 | 'image': '/images/bg_14.png',
214 | 'address': 'Wiesenhüttenstraße 39',
215 | 'city': 'Frankfurt am Main',
216 | 'state': 'Hessen',
217 | 'postalCode': '60329',
218 | 'country': 'Deutschland',
219 | 'category': 'AfroShop',
220 | 'rating': 1.5,
221 | 'numReviews': 5,
222 | 'description':'No description for now - Coming soon!',
223 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
224 | 'phone': '06926010327',
225 | },
226 | {
227 | '_id': '15',
228 | 'name': 'Kaneshie African Shop',
229 | 'image': '/images/bg_15.png',
230 | 'address': 'Moselstraße 51',
231 | 'city': 'Frankfurt am Main',
232 | 'state': 'Hessen',
233 | 'postalCode': '60329',
234 | 'country': 'Deutschland',
235 | 'category': 'AfroShop',
236 | 'rating': 2.5,
237 | 'numReviews': 1,
238 | 'description':'No description for now - Coming soon!',
239 | 'hours': 'Mo - Sa : 09.30 - 20.00 Uhr',
240 | 'phone': '03053606308',
241 | },
242 | {
243 | '_id': '16',
244 | 'name': 'Afro Shop Fouta',
245 | 'image': '/images/bg_16.png',
246 | 'address': 'Oestermärsch 25',
247 | 'city': 'Dortmund',
248 | 'state': 'NRW',
249 | 'postalCode': '44145',
250 | 'country': 'Deutschland',
251 | 'category': 'AfroShop',
252 | 'rating': 5.0,
253 | 'numReviews': 1,
254 | 'description':'No description for now - Coming soon!',
255 | 'hours': 'Mo - Sa : 11.00 - 21.00 Uhr',
256 | 'phone': '023153098523',
257 | },
258 | {
259 | '_id': '17',
260 | 'name': 'Afro World DO.',
261 | 'image': '/images/bg_16.png',
262 | 'address': 'Borsigstraße. 62',
263 | 'city': 'Dortmund',
264 | 'state': 'NRW',
265 | 'postalCode': '44145',
266 | 'country': 'Deutschland',
267 | 'category': 'AfroShop',
268 | 'rating': 2.0,
269 | 'numReviews': 13,
270 | 'description':'No description for now - Coming soon!',
271 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
272 | 'phone': '02314759235',
273 | },
274 | {
275 | '_id': '18',
276 | 'name': 'Max Afroshop',
277 | 'image': '/images/bg_16.png',
278 | 'address': 'Gnadenort 11',
279 | 'city': 'Dortmund',
280 | 'state': 'NRW',
281 | 'postalCode': '44135',
282 | 'country': 'Deutschland',
283 | 'category': 'AfroShop',
284 | 'rating': 5.0,
285 | 'numReviews': 1,
286 | 'description':'No description for now - Coming soon!',
287 | 'hours': 'Mo - Sa : 10.00 - 20.00 Uhr',
288 | 'phone': '02312063967',
289 | },
290 | ]
291 |
292 |
293 | export default shops
294 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, combineReducers, applyMiddleware } from 'redux'
2 | import thunk from 'redux-thunk'
3 | import { composeWithDevTools } from 'redux-devtools-extension'
4 |
5 | import {
6 | shopListReducer,
7 | shopDetailsReducer,
8 | shopReviewCreateReducer,
9 | shopTopRatedReducer } from './reducers/shopReducers'
10 |
11 | import {
12 | productListReducer,
13 | productDetailsReducer,
14 | productDeleteReducer,
15 | productCreateReducer,
16 | productUpdateReducer,
17 | productReviewCreateReducer,
18 | productTopRatedReducer } from './reducers/productReducers'
19 |
20 | import { cartReducer } from './reducers/cartReducers'
21 |
22 | import {
23 | userLoginReducer,
24 | userRegisterReducer,
25 | userDetailsReducer,
26 | userUpdateProfileReducer,
27 | userListReducer,
28 | userDeleteReducer,
29 | userUpdateReducer } from './reducers/userReducers'
30 |
31 | import {
32 | orderCreateReducer,
33 | orderDetailsReducer,
34 | orderPayReducer,
35 | orderListMyReducer,
36 | orderListReducer,
37 | orderDeliverReducer } from './reducers/orderReducers'
38 |
39 | const reducer = combineReducers({
40 | shopList: shopListReducer,
41 | shopDetails: shopDetailsReducer,
42 | shopReviewCreate: shopReviewCreateReducer,
43 | shopTopRated: shopTopRatedReducer,
44 |
45 | cart: cartReducer,
46 |
47 | orderCreate: orderCreateReducer,
48 | orderDetails: orderDetailsReducer,
49 | orderPay: orderPayReducer,
50 | orderListMy: orderListMyReducer,
51 | orderList: orderListReducer,
52 | orderDeliver: orderDeliverReducer,
53 |
54 | productList: productListReducer,
55 | productDetails: productDetailsReducer,
56 | productDelete: productDeleteReducer,
57 | productCreate: productCreateReducer,
58 | productUpdate: productUpdateReducer,
59 | productReviewCreate: productReviewCreateReducer,
60 | productTopRated: productTopRatedReducer,
61 |
62 | userLogin: userLoginReducer,
63 | userRegister: userRegisterReducer,
64 | userDetails: userDetailsReducer,
65 | userUpdateProfile: userUpdateProfileReducer,
66 | userList: userListReducer,
67 | userDelete: userDeleteReducer,
68 | userUpdate: userUpdateReducer,
69 | })
70 |
71 | const cartItemsFromStorage = localStorage.getItem('cartItems') ?
72 | JSON.parse(localStorage.getItem('cartItems')) : []
73 |
74 |
75 | const userInfoFromStorage = localStorage.getItem('userInfo') ?
76 | JSON.parse(localStorage.getItem('userInfo')) : null
77 |
78 |
79 | const shippingAddressFromStorage = localStorage.getItem('shippingAddress') ?
80 | JSON.parse(localStorage.getItem('shippingAddress')) : {}
81 |
82 |
83 | const initialState = {
84 | cart: {
85 | cartItems: cartItemsFromStorage,
86 | shippingAddress: shippingAddressFromStorage,
87 | },
88 | userLogin: { userInfo: userInfoFromStorage },
89 | }
90 |
91 |
92 | const middleware = [thunk]
93 |
94 | const store = createStore(reducer, initialState,
95 | composeWithDevTools(applyMiddleware(...middleware)))
96 |
97 | export default store
--------------------------------------------------------------------------------