#4 routing for hw18 done

Merged
Sergei-Levshnia merged 5 commits from Sergei-Levshnia/hw18-routing-implement into Sergei-Levshnia/master 2 years ago

+ 195 - 0
hw18-react-store/package-lock.json

@@ -15,6 +15,7 @@
         "react": "^17.0.2",
         "react-dom": "^17.0.2",
         "react-redux": "^7.2.6",
+        "react-router-dom": "^5.3.0",
         "react-scripts": "5.0.0",
         "redux": "^4.1.2",
         "redux-thunk": "^2.4.1",
@@ -8215,6 +8216,19 @@
         "he": "bin/he"
       }
     },
+    "node_modules/history": {
+      "version": "4.10.1",
+      "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
+      "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
+      "dependencies": {
+        "@babel/runtime": "^7.1.2",
+        "loose-envify": "^1.2.0",
+        "resolve-pathname": "^3.0.0",
+        "tiny-invariant": "^1.0.2",
+        "tiny-warning": "^1.0.0",
+        "value-equal": "^1.0.1"
+      }
+    },
     "node_modules/hoist-non-react-statics": {
       "version": "3.3.2",
       "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@@ -11385,6 +11399,19 @@
         "node": ">=4"
       }
     },
+    "node_modules/mini-create-react-context": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
+      "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.12.1",
+        "tiny-warning": "^1.0.3"
+      },
+      "peerDependencies": {
+        "prop-types": "^15.0.0",
+        "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+      }
+    },
     "node_modules/mini-css-extract-plugin": {
       "version": "2.4.5",
       "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.5.tgz",
@@ -14020,6 +14047,61 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/react-router": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.1.tgz",
+      "integrity": "sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.12.13",
+        "history": "^4.9.0",
+        "hoist-non-react-statics": "^3.1.0",
+        "loose-envify": "^1.3.1",
+        "mini-create-react-context": "^0.4.0",
+        "path-to-regexp": "^1.7.0",
+        "prop-types": "^15.6.2",
+        "react-is": "^16.6.0",
+        "tiny-invariant": "^1.0.2",
+        "tiny-warning": "^1.0.0"
+      },
+      "peerDependencies": {
+        "react": ">=15"
+      }
+    },
+    "node_modules/react-router-dom": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.0.tgz",
+      "integrity": "sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.12.13",
+        "history": "^4.9.0",
+        "loose-envify": "^1.3.1",
+        "prop-types": "^15.6.2",
+        "react-router": "5.2.1",
+        "tiny-invariant": "^1.0.2",
+        "tiny-warning": "^1.0.0"
+      },
+      "peerDependencies": {
+        "react": ">=15"
+      }
+    },
+    "node_modules/react-router/node_modules/isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+    },
+    "node_modules/react-router/node_modules/path-to-regexp": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
+      "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
+      "dependencies": {
+        "isarray": "0.0.1"
+      }
+    },
+    "node_modules/react-router/node_modules/react-is": {
+      "version": "16.13.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+    },
     "node_modules/react-scripts": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz",
@@ -14523,6 +14605,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/resolve-pathname": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
+      "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
+    },
     "node_modules/resolve-url-loader": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz",
@@ -16087,6 +16174,16 @@
       "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
       "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
     },
+    "node_modules/tiny-invariant": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz",
+      "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg=="
+    },
+    "node_modules/tiny-warning": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+      "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+    },
     "node_modules/tmp": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
@@ -16504,6 +16601,11 @@
         "spdx-expression-parse": "^3.0.0"
       }
     },
+    "node_modules/value-equal": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
+      "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
+    },
     "node_modules/vary": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -23388,6 +23490,19 @@
       "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
       "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
     },
+    "history": {
+      "version": "4.10.1",
+      "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
+      "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
+      "requires": {
+        "@babel/runtime": "^7.1.2",
+        "loose-envify": "^1.2.0",
+        "resolve-pathname": "^3.0.0",
+        "tiny-invariant": "^1.0.2",
+        "tiny-warning": "^1.0.0",
+        "value-equal": "^1.0.1"
+      }
+    },
     "hoist-non-react-statics": {
       "version": "3.3.2",
       "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@@ -25693,6 +25808,15 @@
       "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
       "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="
     },
+    "mini-create-react-context": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
+      "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
+      "requires": {
+        "@babel/runtime": "^7.12.1",
+        "tiny-warning": "^1.0.3"
+      }
+    },
     "mini-css-extract-plugin": {
       "version": "2.4.5",
       "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.5.tgz",
@@ -27463,6 +27587,57 @@
       "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
       "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A=="
     },
+    "react-router": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.1.tgz",
+      "integrity": "sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ==",
+      "requires": {
+        "@babel/runtime": "^7.12.13",
+        "history": "^4.9.0",
+        "hoist-non-react-statics": "^3.1.0",
+        "loose-envify": "^1.3.1",
+        "mini-create-react-context": "^0.4.0",
+        "path-to-regexp": "^1.7.0",
+        "prop-types": "^15.6.2",
+        "react-is": "^16.6.0",
+        "tiny-invariant": "^1.0.2",
+        "tiny-warning": "^1.0.0"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+        },
+        "path-to-regexp": {
+          "version": "1.8.0",
+          "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
+          "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
+          "requires": {
+            "isarray": "0.0.1"
+          }
+        },
+        "react-is": {
+          "version": "16.13.1",
+          "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+          "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+        }
+      }
+    },
+    "react-router-dom": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.0.tgz",
+      "integrity": "sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ==",
+      "requires": {
+        "@babel/runtime": "^7.12.13",
+        "history": "^4.9.0",
+        "loose-envify": "^1.3.1",
+        "prop-types": "^15.6.2",
+        "react-router": "5.2.1",
+        "tiny-invariant": "^1.0.2",
+        "tiny-warning": "^1.0.0"
+      }
+    },
     "react-scripts": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz",
@@ -27851,6 +28026,11 @@
       "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
       "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="
     },
+    "resolve-pathname": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
+      "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
+    },
     "resolve-url-loader": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz",
@@ -29034,6 +29214,16 @@
       "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
       "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
     },
+    "tiny-invariant": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz",
+      "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg=="
+    },
+    "tiny-warning": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+      "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+    },
     "tmp": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
@@ -29357,6 +29547,11 @@
         "spdx-expression-parse": "^3.0.0"
       }
     },
+    "value-equal": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
+      "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
+    },
     "vary": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

+ 1 - 0
hw18-react-store/package.json

@@ -10,6 +10,7 @@
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
     "react-redux": "^7.2.6",
+    "react-router-dom": "^5.3.0",
     "react-scripts": "5.0.0",
     "redux": "^4.1.2",
     "redux-thunk": "^2.4.1",

+ 183 - 64
hw18-react-store/src/App.js

@@ -1,10 +1,11 @@
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
 import logo from './logo.svg';
 import './App.scss';
 import thunk from 'redux-thunk';
 import {createStore, combineReducers, applyMiddleware} from 'redux';
 import {Provider, connect} from 'react-redux';
-
+import { Link, Route, Router, Switch, Redirect } from 'react-router-dom';
+import createHistory from 'history/createBrowserHistory'
 
 const actionPending  = name => ({type: 'PROMISE', status: 'PENDING', name})
 const actionResolved = (name, payload) => ({type: 'PROMISE', status: 'RESOLVED', name, payload})
@@ -141,8 +142,11 @@ function promiseReducer(state={}, {type, name, status, payload, error}){
 const actionAuthLogin = (token) => ({ type: 'AUTH_LOGIN', token })
 const actionAuthLogout = () => ({ type: 'AUTH_LOGOUT' })
 
-const actionLogin = (login = 'tst', password = '123') =>
-    actionPromise('login', gql(`query ($login:String, $password:String){ login(login:$login, password:$password)}`, { 'login': login, 'password': password }))
+const actionLogin = (login, password) =>
+    actionPromise('login', gql(`
+    query log($login:String, $password:String) {
+        login(login: $login, password: $password)
+      }`, { login, password }))
 
 const actionFullLogin = (login = 'tst', password = '123') =>
     async dispatch => {
@@ -152,12 +156,18 @@ const actionFullLogin = (login = 'tst', password = '123') =>
         }
     }
 
-const actionRegister = (login = 'tst', password = '123') =>
-    actionPromise('login', gql(`mutation reg($login:String, $password:String) {
-        UserUpsert(user:{login:$login, password:$password, nick:$login}){
-          _id login
+const actionRegister = (login, password) =>
+    actionPromise('registration', gql(`
+        mutation register($login:String, $password:String) {
+            UserUpsert(
+            user: {
+                login: $login,
+                password: $password,
+            }){
+                login _id 
+            }
         }
-      }`, { 'login': login, 'password': password }))
+      `, { login, password }))
 
 const actionFullRegister = (login = 'tst', password = '123') =>
     async dispatch => {
@@ -180,13 +190,20 @@ const actionCatById = (_id) =>
     }`, { q: JSON.stringify([{ _id }]) }))
 
 const actionGoodById = (_id) =>
-    actionPromise('goodById', gql(`query ($good:String) {
-        GoodFindOne(query:$good) {
-          _id name price images {
-              url
-          }
-        }
-      }`, { good: JSON.stringify([{ _id }]) }))
+    actionPromise('goodById', gql(`
+        query goodById ($good:String) {
+            GoodFindOne(query: $good) {
+                name 
+                description 
+                price
+                categories {
+                    name
+                } 
+                images {
+                    url
+                }
+            }
+        }`, { good: JSON.stringify([{ _id }]) }))
 
 
 const store = createStore(combineReducers({promise: promiseReducer, 
@@ -194,24 +211,29 @@ const store = createStore(combineReducers({promise: promiseReducer,
                                             cart: cartReducer}), applyMiddleware(thunk)) 
 store.subscribe(() => console.log(store.getState()))
 store.dispatch(actionRootCats())
-store.dispatch(actionCatById('5dc458985df9d670df48cc47'))
 
 
 const Logo = () =>
-    <img src={logo} className="Logo" alt="logo" />
+    <Link to="/">
+        <img src={logo} className="Logo" alt="logo" />
+    </Link>
 
 const Header = () =>
-    <header><Logo /></header>
+    <header>
+        <Logo />
+    </header>
 
 const Navbar = () =>
     <nav className='Navbar'>
         <CKoshik/>
+        <LogBtn />
+        <RegBtn />
     </nav>
-
+//--------------------------------------------------------------------------------------------------------------------------------
 const CategoryListItem = ({_id, name}) =>
-    <li className='CatLink'>
-        <a href={`#/category/${_id}`}>{name}</a>
-    </li>
+    <Link to={`/category/:${_id}`}>
+        <li className='CatLink' style={{color:'dodgerblue'}}>{name}</li>
+    </Link>
 
 const CategoryList = ({cats}) =>
     <ul>{cats.map((item) => <CategoryListItem {...item}/>)}</ul>
@@ -220,11 +242,13 @@ const CCategoryList = connect(state => ({cats:state.promise.rootCats?.payload ||
 
 const Aside = () =>
     <aside><CCategoryList /></aside>
-
+//--------------------------------------------------------------------------------------------------------------------------------
 const GoodCard = ({good:{_id, name, price, images}, onAdd}) =>
     <li className='GoodCard'> 
         <h2>{name}</h2>
-        {images && images[0] && images[0].url && <img className='GoodImg' src={backendURL + '/' + images[0].url} />}
+        <Link to={`/good/:${_id}`}>На страницу товара</Link>
+        <br/>
+        {images && images[0] && images[0].url && <img className='GoodImg' alt='img' src={backendURL + '/' + images[0].url} />}
         <br/>
         <strong>Цена: {price}</strong>
         <br/>
@@ -232,14 +256,45 @@ const GoodCard = ({good:{_id, name, price, images}, onAdd}) =>
     </li>
 
 const CGoodCard = connect(null, {onAdd: actionCartAdd})(GoodCard)
-
-const Category = ({cat:{name, goods=[]}={}}) => 
-    <div className='Category'>
-        <h1>{name}</h1>
-        <ul>{goods.map(good => <CGoodCard good={good} />)}</ul>
+//--------------------------------------------------------------------------------------------------------------------------------
+const GoodPg = ({good:{_id, name, price, images, description}, onAdd}) =>
+    <div className='GoodCard'> 
+        <h2>{name}</h2>
+        <br/>
+        {images && images[0] && images[0].url && <img className='GoodImg' alt='img' src={backendURL + '/' + images[0].url} />}
+        <br/>
+        <strong>Цена: {price}</strong>
+        <br/>
+        <p>{description}</p>
+        <br />
+        <button onClick={() => onAdd({_id, name, price, images})}>Добавить в корзину</button>
     </div>
 
-const CCategory = connect(state => ({cat:state.promise.catById?.payload || {}}))(Category)
+const CGoodPg = connect(state => ({good:state.promise.goodById?.payload || {}}),{onAdd: actionCartAdd})(GoodPg)
+
+const PageGood = ({match: {params: {_id}}, getData}) => {
+    useEffect(() => {
+        getData(_id.substring(1))
+        console.log('get', _id,typeof _id)
+    },[_id])
+    return (
+        <CGoodPg />
+    )
+
+}
+
+const CPageGood = connect(null, {getData: actionGoodById})(PageGood)
+//--------------------------------------------------------------------------------------------------------------------------------
+const LogBtn = () =>
+    <Link to="/login" style={{color:'white', textDecoration:'none'}}>
+        <div className='KoshikCnt item'>Войти / Выйти</div>
+    </Link>
+
+const RegBtn = () =>
+    <Link to="/registration" style={{color:'white', textDecoration:'none'}}>
+        <div className='KoshikCnt item'>Зарегистрироваться</div>
+    </Link>
+
 
 const Koshik = ({cart}) => {
     let goodsInCart = cart
@@ -247,28 +302,54 @@ const Koshik = ({cart}) => {
     for (let key in goodsInCart) {
         allGoodsInCart += goodsInCart[key].count
     }
-
     return (
-        <div className='KoshikCnt item'>
-            <h3>Корзина: {allGoodsInCart}</h3>
-        </div>
+        <Link to="/cart" style={{color:'white', textDecoration:'none'}}>
+            <div className='KoshikCnt item'>
+                Корзина: {allGoodsInCart}
+            </div>
+        </Link>
     )
 }
 
 const CKoshik = connect(({cart}) => ({cart}))(Koshik)
+//--------------------------------------------------------------------------------------------------------------------------------
+const Category = ({cat:{name, goods=[]}={}}) => 
+    <div className='Category'>
+        <h1>{name}</h1>
+        <ul>
+            {(goods || []).map(good => <CGoodCard good={good} />)}
+        </ul>
+    </div>
+
+const CCategory = connect(state => ({cat:state.promise.catById?.payload || {}}))(Category)
+
+const PageCategory = ({match: {params: {_id}}, getData}) => {
+    useEffect(() => {
+        getData(_id.substring(1))
+        console.log('get', _id,typeof _id)
+    },[_id])
+    return (
+        <CCategory />
+    )
 
+}
+
+const CPageCategory = connect(null, {getData: actionCatById})(PageCategory)
+//--------------------------------------------------------------------------------------------------------------------------------
 const CartItem = ({cart:{_id, name, price, images}, count: {count}, onChange, onRemove}) => {
     console.log('good', _id)
     return(
         <li className='GoodCard'> 
             <h2>{name}</h2>
-            {images && images[0] && images[0].url && <img className='GoodImg' src={backendURL + '/' + images[0].url} />}
+            <Link to={`/good/:${_id}`}>На страницу товара</Link>
+            <br/>
+            {images && images[0] && images[0].url && <img className='GoodImg' alt='img' src={backendURL + '/' + images[0].url} />}
             <br/>
             <strong>Цена: {price * count}</strong>
             <br/>
             <label>Кол-во покупки: <input type="number" value={count} min="1" onInput={(e) => onChange({_id, name, price, images}, e.target.value)}/></label>
             <br/>
-            <button>Заказать</button>
+            <button disabled={!localStorage.authToken}>{localStorage.authToken? 'Заказать' : 'Авторизуйтесть чтобы заказать'}</button>
             <button onClick={() => onRemove({_id, name, price, images})}>Удалить заказ[X]</button>
         </li>
     )
@@ -281,65 +362,103 @@ const Cart = ({cart}) => {
     for(let item in cart) {
         cartArr.push(cart[item])
     }
-    console.log('cartarr',cartArr)
-    
     return(
         <div>
-            <h1>Корзина</h1>
+            <h1 style={{marginLeft:'30px'}}>Корзина</h1>
             <ul>{cartArr.map(item => <CCartItem cart={item.good} count={item} />)}</ul>
         </div>
     )
 }
 
 const CCart = connect(state => ({cart:state.cart}))(Cart)
-//const CCart = connect(забрать из редакса корзину положить в пропс cart, 
-                       //дать компоненту onCartChange и onCartRemove с соответствующими actionCreator)(Cart)
-
-
-const LoginForm = ({onLogin}) => {
+//--------------------------------------------------------------------------------------------------------------------------------
+const LoginForm = ({log:{sub}, onLogin, onLogout}) => {
     let [pass, setPass] = useState()
     let [login, setLogin] = useState()
     return (
-        <div className='form'>
-            <h3>Login Form</h3>
-            <input placeholder='login' style={{outlineColor: login? 'black' : 'firebrick'}} onChange={(e) => setLogin(e.target.value)}/>
+        <div className='form' style={{margin: '0 auto'}}>
+            <h3>Войти</h3>
+            <input placeholder='Логин' style={{outlineColor: login? 'black' : 'firebrick'}} onChange={(e) => setLogin(e.target.value)}/>
+            <br/>
+            <input placeholder='Пароль' type="password" style={{outlineColor: pass? 'black' : 'firebrick'}} onChange={(e) => setPass(e.target.value)}/>
+            <br/>
+            <button disabled={!pass || !login || localStorage.authToken} onClick={() => onLogin(login, pass)}>{localStorage.authToken? 'Авторизация была выполнена.' : 'Войти'}</button>
+            {localStorage.authToken && <button onClick={() => onLogout()}>Выйти</button>}
+            <br/>
+            {sub && <small>Пользователь {sub.login} авторизован</small>}
+        </div>
+    )
+}
+
+const CLoginForm = connect(state => ({log: state.auth?.payload || {}}), {onLogin: actionFullLogin, onLogout: actionAuthLogout})(LoginForm)
+//--------------------------------------------------------------------------------------------------------------------------------
+const RegForm = ({reg:{login}, loged: {sub}, onRegister, onFullRegister}) => {
+    let [pass, setPass] = useState()
+    let [log, setLog] = useState()
+    return (
+        <div className='form' style={{margin: '0 auto'}}>
+            <h3>Зарегистрироваться</h3>
+            <input placeholder='Логин' style={{outlineColor: log? 'black' : 'firebrick'}} onChange={(e) => setLog(e.target.value)}/>
+            <br/>
+            <input placeholder='Пароль' type="password" style={{outlineColor: pass? 'black' : 'firebrick'}} onChange={(e) => setPass(e.target.value)}/>
+            <br/>
+            <button disabled={!pass || !log || localStorage.authToken} onClick={() => onRegister(log, pass)}>{localStorage.authToken? 'Авторизация была выполнена' : 'Зарегистрироваться'}</button>
             <br/>
-            <input placeholder='password' type="password" style={{outlineColor: pass? 'black' : 'firebrick'}} onChange={(e) => setPass(e.target.value)}/>
+            {!localStorage.authToken && <button disabled={!pass || !log} onClick={() => {onFullRegister(log, pass); }}>Зарегистрироваться и Войти</button>}
             <br/>
-            <button disabled={!pass || !login} onClick={() => onLogin(login, pass)}>Login</button>            
+            {login && `Пользователь ${login} зарегистрирован ${sub && 'и авторизован'}!`}
         </div>
     )
 }
 
-const CLoginForm = connect(null, {onLogin: actionFullLogin})(LoginForm)
+const CRegForm = connect(state => ({reg: state.promise.registration?.payload || {}, loged: state.auth?.payload || {}}), {onRegister: actionRegister, onFullRegister: actionFullRegister})(RegForm)
+//--------------------------------------------------------------------------------------------------------------------------------
+
+
+const PageMain = () => <h1>MAIN PAGE</h1>
+
+const Page404 = () => <h1> 404 </h1>
 
 const Main = () =>
     <main>
         <Aside />
-        <Content>
-            <CCategory />
-            {/* <LoginForm onLogin={(l, p) => actionFullLogin(l, p)}/> */}
-            <CLoginForm />
-            <CCart />
+        <Content style={{border:'1px solid black'}}>
+            <Switch>
+                <Redirect from='/main' to='/' />
+                <Route path="/" component={PageMain} exact/>
+                <Route path="/category/:_id" component={CPageCategory}/>
+                <Route path="/good/:_id" component={CPageGood}/>
+                <Route path="/cart" component={CCart} />
+                <Route path="/login" component={CLoginForm} />
+                <Route path="/registration" component={CRegForm} />
+                <Route path="*" component={Page404} />
+            </Switch>
         </Content>
     </main>
 
 const Content = ({children}) =>
     <div className="Content">{children}</div>
 
+
 const Footer = () =>
     <footer><Logo /></footer>
 
+
+const history = createHistory()
+
+
 function App() {
     return (
-        <Provider store={store}>
-            <div className="App">
-                <Header />
-                <Navbar />
-                <Main />
-                <Footer />
-            </div>
-        </Provider>
+        <Router history={history}>
+            <Provider store={store}>
+                <div className="App">
+                    <Header />
+                    <Navbar />
+                    <Main />
+                    <Footer />
+                </div>
+            </Provider>
+        </Router>
     );
 }
 

+ 6 - 0
hw18-react-store/src/App.scss

@@ -118,4 +118,10 @@ li {
   text-align: center;
   border: 5px solid rgb(47, 121, 170);
   padding: 5px;
+  margin-left: 30px;
 }
+
+.Content {
+  padding: 10px;
+  margin: 0 auto;
+}