ソースを参照

initial commit

Aleksandrov96 2 年 前
コミット
130e488d4a
44 ファイル変更12932 行追加33 行削除
  1. 23 0
      nix-project/.gitignore
  2. 70 0
      nix-project/README.md
  3. 11654 0
      nix-project/package-lock.json
  4. 45 0
      nix-project/package.json
  5. 15 0
      nix-project/public/index.html
  6. 27 0
      nix-project/src/App.js
  7. 116 0
      nix-project/src/App.scss
  8. 157 0
      nix-project/src/actions/GQLActions.js
  9. 2 0
      nix-project/src/actions/actionsAuthReducer.js
  10. 4 0
      nix-project/src/actions/actionsCartReducer.js
  11. 16 0
      nix-project/src/actions/actionsPromiseReducer.js
  12. 10 0
      nix-project/src/components/Aside/Aside.js
  13. 22 0
      nix-project/src/components/CartCounter/CartCounter.js
  14. 15 0
      nix-project/src/components/Category/Category.js
  15. 9 0
      nix-project/src/components/Footer/Footer.js
  16. 18 0
      nix-project/src/components/Good/Good.js
  17. 20 0
      nix-project/src/components/GoodCard/GoodCard.js
  18. 24 0
      nix-project/src/components/GoodInCart/GoodInCart.js
  19. 22 0
      nix-project/src/components/Header/Header.js
  20. 28 0
      nix-project/src/components/LoginButtons/LoginButtons.js
  21. 22 0
      nix-project/src/components/LoginForm/LoginForm.js
  22. 30 0
      nix-project/src/components/Main/Main.js
  23. 0 0
      nix-project/src/components/Pages/Admin/Admin.js
  24. 20 0
      nix-project/src/components/Pages/Cart/Cart.js
  25. 9 0
      nix-project/src/components/Pages/Content/Content.js
  26. 8 0
      nix-project/src/components/Pages/Page404/Page404.js
  27. 18 0
      nix-project/src/components/Pages/PageCategory/PageCategory.js
  28. 16 0
      nix-project/src/components/Pages/PageGood/PageGood.js
  29. 6 0
      nix-project/src/components/Pages/PageMain/PageMain.js
  30. 61 0
      nix-project/src/components/Pages/PageOrderHistory/OrderHistoryItem.js
  31. 26 0
      nix-project/src/components/RegForm/RegForm.js
  32. 24 0
      nix-project/src/components/RootCategories/RootCategories.js
  33. 9 0
      nix-project/src/components/RootCategory/RootCategory.js
  34. 37 0
      nix-project/src/components/Search/Search.js
  35. 9 0
      nix-project/src/components/SubCategories/SubCategories.js
  36. BIN
      nix-project/src/img/logo.png
  37. 8 0
      nix-project/src/index.js
  38. 37 0
      nix-project/src/reducers/authReducer.js
  39. 42 0
      nix-project/src/reducers/cartReducer.js
  40. 11 0
      nix-project/src/reducers/promiseReducer.js
  41. 15 0
      nix-project/src/reducers/store.js
  42. 20 0
      nix-project/src/services/getGQL.js
  43. 167 33
      store/src/App.js
  44. 40 0
      store/src/App.scss

+ 23 - 0
nix-project/.gitignore

@@ -0,0 +1,23 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*

+ 70 - 0
nix-project/README.md

@@ -0,0 +1,70 @@
+# Getting Started with Create React App
+
+This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
+
+## Available Scripts
+
+In the project directory, you can run:
+
+### `npm start`
+
+Runs the app in the development mode.\
+Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
+
+The page will reload when you make changes.\
+You may also see any lint errors in the console.
+
+### `npm test`
+
+Launches the test runner in the interactive watch mode.\
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
+
+### `npm run build`
+
+Builds the app for production to the `build` folder.\
+It correctly bundles React in production mode and optimizes the build for the best performance.
+
+The build is minified and the filenames include the hashes.\
+Your app is ready to be deployed!
+
+See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
+
+### `npm run eject`
+
+**Note: this is a one-way operation. Once you `eject`, you can't go back!**
+
+If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
+
+Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
+
+You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
+
+## Learn More
+
+You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
+
+To learn React, check out the [React documentation](https://reactjs.org/).
+
+### Code Splitting
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
+
+### Analyzing the Bundle Size
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
+
+### Making a Progressive Web App
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
+
+### Advanced Configuration
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
+
+### Deployment
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
+
+### `npm run build` fails to minify
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

ファイルの差分が大きいため隠しています
+ 11654 - 0
nix-project/package-lock.json


+ 45 - 0
nix-project/package.json

@@ -0,0 +1,45 @@
+{
+  "name": "nix-project",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "@testing-library/jest-dom": "^5.16.1",
+    "@testing-library/react": "^12.1.2",
+    "@testing-library/user-event": "^13.5.0",
+    "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",
+    "web-vitals": "^2.1.2"
+  },
+  "scripts": {
+    "start": "react-scripts start",
+    "build": "react-scripts build",
+    "test": "react-scripts test",
+    "eject": "react-scripts eject"
+  },
+  "eslintConfig": {
+    "extends": [
+      "react-app",
+      "react-app/jest"
+    ]
+  },
+  "browserslist": {
+    "production": [
+      ">0.2%",
+      "not dead",
+      "not op_mini all"
+    ],
+    "development": [
+      "last 1 chrome version",
+      "last 1 firefox version",
+      "last 1 safari version"
+    ]
+  },
+  "devDependencies": {
+    "sass": "^1.45.2"
+  }
+}

+ 15 - 0
nix-project/public/index.html

@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <meta
+      name="description"
+      content="Web site created using create-react-app"
+    />
+    <title>React App</title>
+  </head>
+  <body>
+    <div id="root"></div>
+  </body>
+</html>

+ 27 - 0
nix-project/src/App.js

@@ -0,0 +1,27 @@
+import React from "react";
+import './App.scss';
+import { Provider } from 'react-redux';
+import { Router } from "react-router-dom";
+import store from "./reducers/store";
+import Header from "./components/Header/Header";
+import Main from "./components/Main/Main";
+import Footer from "./components/Footer/Footer";
+import createHistory from "history/createBrowserHistory";
+
+const history = createHistory()
+
+function App() {
+  return (
+    <Router history={history}>
+      <Provider store={store}> 
+        <div className="App">
+          <Header />
+          <Main />
+          <Footer />
+        </div>
+      </Provider>
+    </Router>
+  );
+}
+
+export default App;

+ 116 - 0
nix-project/src/App.scss

@@ -0,0 +1,116 @@
+.GoodCard {
+border: 1px solid black;
+margin: 2rem;
+
+    img{
+        max-width: 50%;
+    }
+}
+
+.App {
+
+    footer{
+        background-color: #303030;
+        
+        .name {
+            font-size: 10rem;
+        }
+    }
+
+    header {        
+        img {
+            width: 100%;
+            max-width: 100px;
+            height: auto; 
+        }
+    }
+
+    main {
+        display: flex;
+        flex-direction: row;
+        aside {
+            width: 30%;
+        
+                .link {
+                    display: block;
+                    color: white;
+                    padding: 15px 20px;
+                    text-decoration: none;
+                    &:hover {
+                        background-color: rgb(255, 255, 255);
+                        color: black;
+                    }
+                }
+
+        }
+    }
+}
+
+li {
+list-style-type: none;
+}
+
+.RootCategories {
+display: block;
+background-color: black;
+}
+
+.Koshik {
+    text-decoration: none;
+    color: white;
+    border: 1px solid black;
+    background-color: black;
+    border-radius: 50%;
+    padding: .5rem;
+}
+
+.loginBtn {
+    color: white;
+    background-color: black;
+    border-radius: .2rem;
+    text-decoration: none;
+    &:hover {
+        background-color: rgb(255, 255, 255);
+        color: black;
+    }
+}
+
+.loginButtons {
+    display: flex;
+    flex-direction: column;
+
+    .btn {
+        width: 100%;
+        background-color: black; 
+        border: none;
+        color: white;
+        padding: 15px 32px;
+        text-align: center;
+        text-decoration: none;
+        display: inline-block;
+        font-size: 16px;
+
+        &:hover {
+            background-color: white; 
+            color: black;
+        }
+    }
+}
+
+.exit {
+    display: block;
+    width: 100%;
+    background-color: black; 
+    border: none;
+    color: white;
+    padding: 15px 32px;
+    text-align: center;
+    text-decoration: none;
+    display: inline-block;
+    font-size: 16px;
+
+    &:hover {
+        background-color: white; 
+        color: black;
+    }
+}

+ 157 - 0
nix-project/src/actions/GQLActions.js

@@ -0,0 +1,157 @@
+import { gql } from "../services/getGQL";
+import { actionAuthLogin } from "./actionsAuthReducer";
+import { actionPromise } from "./actionsPromiseReducer";
+
+const actionLogin = (login, password) => 
+    actionPromise('login', gql(`query log($login: String, $password: String) {
+        login(login: $login, password: $password)
+        }`, {login, password}));
+        
+const actionFullLogin = (login, password) => 
+async (dispatch) => {
+        let token = await dispatch(actionLogin(login, password));
+        if (token) {
+            console.log(token);
+            dispatch(actionAuthLogin(token));
+        }
+    }
+
+const actionCatById = (_id) =>
+    actionPromise('catById', gql(`query catById($q: String){
+            CategoryFindOne(query: $q){
+                subCategories{name, _id}
+                _id name goods {
+                    _id name price images {
+                        url
+                    }
+                }
+            }
+        }`, { q: JSON.stringify([{ _id }]) }))
+
+const actionRootCats = () =>
+actionPromise('rootCats', gql(`query {
+  CategoryFind(query: "[{\\"parent\\":null}]"){
+    _id name
+  }
+}`))
+
+const actionGoodById = (_id) => actionPromise("goodById", gql(
+  `query goodById($q: String) {
+ GoodFindOne(query: $q) {
+  _id name description price categories {
+    _id
+    name
+  } images {
+    _id
+    url
+  } 
+}
+  }`,
+  { q: JSON.stringify([{ _id }]) }
+  )
+);
+
+const actionGoodFind = (word) => (
+  actionPromise('goodFind', gql(`query goodById($q: String) {
+      GoodFind(query: $q) {
+          _id name price description images {
+          url
+          }
+      }
+  }`, {q: JSON.stringify([
+          {
+              $or: [{title: `/${word}/`}, {name: `/${word}/`}] 
+          },
+          {
+              sort: [{title: 1}]  
+          } 
+          ])
+      }
+  ))
+)
+
+const actionReg = (login, password) => {
+  return actionPromise(
+      "reg",
+      gql(
+          `mutation reg($l: String, $p: String){
+      UserUpsert(user: {login: $l,
+                        password:$p
+                      }){
+        _id login
+      }
+    }`,
+          { l: login, p: password }
+      )
+  );
+};
+
+const actionFullReg = (login, password) =>
+    async function a(dispatch) {
+        try {
+            await dispatch(actionReg(login, password));
+        } catch (e) {
+            return 0;
+        }
+        await dispatch(actionFullLogin(login, password));
+    }
+
+const actionNewOrder = () => async (dispatch, getState) => {
+  let { cart } = getState();
+  const orderGoods = Object.entries(cart).map(([_id, { count }]) => ({
+      good: { _id },
+      count,
+  }));
+
+  let result =  await dispatch(
+      actionPromise(
+          "order",gql(
+              `mutation newOrder($order:OrderInput){ 
+                            OrderUpsert(order:$order) 
+                            { _id total} 
+                          }`,
+              { order: {orderGoods} }) )
+  );
+};
+  
+const actionGetOrdersHistory = () => actionPromise("orderHistory", gql(`query o { 
+    OrderFind(query: "[{},{\\"sort\\": [{\\"_id\\": -1}]}]") { 
+      createdAt
+        orderGoods{
+            price, total, count
+            good{_id, name, images{_id, url}}
+        }, total
+    }
+  }`)
+)
+
+const actionGoodChange = (name) => {
+  return actionPromise(
+    'goodChange', gql(`mutation g{
+      GoodUpsert(good:{
+        _id: "5dc45c3d5df9d670df48cc4a",
+        name: ${name}
+      }) {
+        _id
+        createdAt
+        name
+        description
+        price
+      }
+    }`)
+  )
+}
+
+export {
+  actionLogin,
+  actionFullLogin,
+  actionCatById,
+  actionRootCats,
+  actionGoodById,
+  actionGoodFind,
+  actionReg,
+  actionFullReg,
+  actionNewOrder,
+  actionGetOrdersHistory,
+  actionGoodChange
+}

+ 2 - 0
nix-project/src/actions/actionsAuthReducer.js

@@ -0,0 +1,2 @@
+export const actionAuthLogin = token => ({ type: 'AUTH_LOGIN', token })
+export const actionAuthLogout = () => ({ type: 'AUTH_LOGOUT' })

+ 4 - 0
nix-project/src/actions/actionsCartReducer.js

@@ -0,0 +1,4 @@
+export const actionCartAdd = (good, count=1) => ({type: "CART_ADD", good, count: 1});
+export const actionCartChange = (good, count) => ({type: 'CART_CHANGE', good, count});
+export const actionCartRemove = (good) => ({type: 'CART_REMOVE', good});
+export const actionCartClear = () => ({type: 'CART_CLEAR'});

+ 16 - 0
nix-project/src/actions/actionsPromiseReducer.js

@@ -0,0 +1,16 @@
+export const actionPending = name => ({ type: 'PROMISE', status: 'PENDING', name })
+export const actionResolved = (name, payload) => ({ type: 'PROMISE', status: 'RESOLVED', name, payload })
+export const actionRejected = (name, error) => ({ type: 'PROMISE', status: 'REJECTED', name, error })
+
+export const actionPromise = (name, promise) =>
+    async dispatch => {
+        dispatch(actionPending(name))
+        try {
+            let data = await promise
+            dispatch(actionResolved(name, data))
+            return data
+        }
+        catch (error) {
+            dispatch(actionRejected(name, error))
+        }
+    }

+ 10 - 0
nix-project/src/components/Aside/Aside.js

@@ -0,0 +1,10 @@
+import React from "react"
+import CRootCategories from "../RootCategories/RootCategories"
+
+const Aside = ({}) => (
+    <aside>
+        <CRootCategories />
+    </aside>
+  )
+
+export default Aside;

+ 22 - 0
nix-project/src/components/CartCounter/CartCounter.js

@@ -0,0 +1,22 @@
+import React from "react";
+import { Link } from 'react-router-dom';
+import { connect } from 'react-redux';
+import logo from '../../img/logo.png';
+
+const CartCounter = ({cart}) => {
+    let count = 0;
+    let sum = Object.entries(cart).map(([, val]) => val.count)
+    count = sum.reduce((a, b) => a + b, 0)
+    return (
+      <>
+          <img className='logo' src={logo} />
+        <Link className='Koshik' to='/korzina'>
+          {count}
+        </Link>
+      </>
+    )
+  }
+
+const CCartCounter = connect(({cart}) => ({cart}))(CartCounter)
+
+export default CCartCounter;

+ 15 - 0
nix-project/src/components/Category/Category.js

@@ -0,0 +1,15 @@
+import React from "react";
+import { connect } from 'react-redux';
+import SubCategories from "../SubCategories/SubCategories";
+import { CGoodCard } from "../GoodCard/GoodCard";
+
+const Category = ({cat: {_id, name, goods, subCategories}={}}) => 
+  <div className='Category'>
+    <h1>{name}</h1>
+    {subCategories && <SubCategories cats={subCategories} />}
+    {(goods || []).map(good => <CGoodCard good={good} />)}
+  </div>
+
+const CCategory = connect(state => ({cat: state.promise.catById?.payload}))(Category)
+
+export default CCategory;

+ 9 - 0
nix-project/src/components/Footer/Footer.js

@@ -0,0 +1,9 @@
+import React from "react";
+
+const Footer = () => (
+    <footer>
+        <h1 className='name' >S T O R E</h1>
+    </footer>
+  )
+
+export default Footer;

+ 18 - 0
nix-project/src/components/Good/Good.js

@@ -0,0 +1,18 @@
+import React from "react";
+import { connect } from "react-redux";
+import { actionCartAdd } from "../../actions/actionsCartReducer";
+import { backURL } from "../../services/getGQL";
+
+const Good = ({ good: { _id, name, price, images, description } = {},onCartAdd }) => (
+    <div className="Good">
+        <h1>{name}</h1>
+        {images && images[0] && images[0].url && <img src={backURL + '/' + images[0].url} />}
+        <h1>{price}</h1>
+        <h1>{description}</h1>
+        <button onClick={() => onCartAdd({_id, name, price, images})}>+</button>
+    </div>
+  );
+  
+const CGood = connect((state) => ({ good: state.promise.goodById?.payload }),{onCartAdd: actionCartAdd})(Good);
+
+export default CGood;

+ 20 - 0
nix-project/src/components/GoodCard/GoodCard.js

@@ -0,0 +1,20 @@
+import React from "react";
+import { Link } from "react-router-dom";
+import { connect } from 'react-redux';
+import { actionCartAdd } from "../../actions/actionsCartReducer";
+import { backURL } from "../../services/getGQL";
+
+const GoodCard = ({good: {_id, name, price, images}={}, onCartAdd}) =>
+  <div className='GoodCard'>
+    <Link to={`/good/${_id}`}><h2>{name}</h2></Link>
+    {images && images[0] && images[0].url && <img src={backURL + '/' + images[0].url} />}
+    <strong>{price} ₴</strong>
+    <button onClick={() => onCartAdd({_id, name, price, images})}>+</button>
+  </div>
+
+const CGoodCard = connect(null, {onCartAdd: actionCartAdd})(GoodCard);
+
+export {
+  GoodCard,
+  CGoodCard
+}

+ 24 - 0
nix-project/src/components/GoodInCart/GoodInCart.js

@@ -0,0 +1,24 @@
+import React from "react";
+import { connect } from 'react-redux';
+import { actionCartAdd, actionCartChange, actionCartRemove, actionCartClear } from "../../actions/actionsCartReducer";
+import { backURL } from "../../services/getGQL";
+
+const GoodInCart =  ({item: {count, good: {_id, name, price, images}}, onCartChange, onCartRemove}) => (
+    <div className='GoodInCart'>
+        <h2>{name}</h2>
+        <strong>{count}</strong> шт. на <strong>{price*count} ₴</strong>
+        {images && images[0] && images[0].url && <img src={backURL + '/' + images[0].url} />}
+        <strong>{price}</strong>
+        <br/>
+        <input onInput={(e) => onCartChange({_id, name, price, images}, e.currentTarget.value)}  value={count} type="number"/>
+        <br/>
+        <button onClick={() => onCartRemove({_id, name, price, images})}>X</button>
+    </div>
+  )
+  
+const CGoodInCart = connect(null, { onCartChange: actionCartChange, 
+                                    onCartRemove: actionCartRemove, 
+                                    onCartAdd: actionCartAdd
+                                    })(GoodInCart)
+
+export default CGoodInCart;

+ 22 - 0
nix-project/src/components/Header/Header.js

@@ -0,0 +1,22 @@
+import React from "react";
+import { Switch, Route } from "react-router-dom";
+import CCartCounter from "../CartCounter/CartCounter";
+import { CSearch } from "../Search/Search";
+import CLoginForm from "../LoginForm/LoginForm";
+import CRegForm from "../RegForm/RegForm";
+import CLoginButtons from "../LoginButtons/LoginButtons";
+
+const Header = () => (
+    <header>
+        <h1 className='name'>S T O R E</h1>
+        <CCartCounter />
+        <CSearch />
+        <Switch>
+          <Route path="/login" component={CLoginForm}/>
+          <Route path="/registration" component={CRegForm}/>
+          <Route path='*' component={CLoginButtons} />
+        </Switch>
+    </header>
+)
+
+export default Header;

+ 28 - 0
nix-project/src/components/LoginButtons/LoginButtons.js

@@ -0,0 +1,28 @@
+import React from "react";
+import { connect } from "react-redux";
+import { Link } from "react-router-dom";
+import { actionAuthLogout } from "../../actions/actionsAuthReducer";
+
+const LoginButtons = ({onLogout, history, token}) => {
+
+    return (
+        <>
+            {!token ?
+                (<div className='loginButtons'>
+                    <Link to='/login'><button className='btn' onClick={() => history.push('/')}>Вход</button></Link>
+                    <Link to='/registration'><button className='btn'>Регистрация</button></Link>
+                </div>) 
+                :
+                (<div className='LoginButtons'>
+                  <strong>{`Привет, ${JSON.parse(atob(token.split(".")[1])).sub.login}!`}</strong>
+                  <Link to='/orderhistory'><button className='exit'>История заказов</button></Link>
+                  <button className='exit' onClick={() => {onLogout(); history.push('/')}}>Выйти</button>
+                </div>)
+            }
+        </>
+    )
+  }
+  
+const CLoginButtons = connect(state => ({token: state.auth.token}), {onLogout: actionAuthLogout})(LoginButtons)
+
+export default CLoginButtons;

+ 22 - 0
nix-project/src/components/LoginForm/LoginForm.js

@@ -0,0 +1,22 @@
+import React from "react";
+import { connect } from "react-redux";
+import { useState } from "react";
+import { actionFullLogin } from "../../actions/GQLActions";
+
+const LoginForm = ({onLogin}) => {
+
+    const [login, setLogin] = useState('');
+    const [password, setPassword] = useState('');
+  
+    return (
+      <div className='LoginForm'>
+        <input type="text" value={login} onChange={e => setLogin(e.target.value)} style={{border: (login.length === 0) ? '1px solid red' : ''}}/>
+        <input type="password" value={password} onChange={e => setPassword(e.target.value)} style={{border: (password.length === 0) ? '1px solid red' : ''}}/>
+        <button className='link' onClick={() => onLogin(login,password)} disabled={login.length == 0 || password.length == 0}>LOGIN</button>
+      </div>
+    )
+  }
+  
+const CLoginForm = connect(null, {onLogin: actionFullLogin})(LoginForm)
+
+export default CLoginForm;

+ 30 - 0
nix-project/src/components/Main/Main.js

@@ -0,0 +1,30 @@
+import React from "react";
+import { Switch, Route, Redirect } from "react-router-dom";
+import Aside from "../Aside/Aside";
+import Content from "../Pages/Content/Content";
+import PageMain from "../Pages/PageMain/PageMain";
+import CPageCategory from "../Pages/PageCategory/PageCategory";
+import CPageGood from "../Pages/PageGood/PageGood";
+import CCart from "../Pages/Cart/Cart";
+import Page404 from "../Pages/Page404/Page404";
+import { CSearchResult } from "../Search/Search";
+import COrderHistiry from "../Pages/PageOrderHistory/OrderHistoryItem";
+
+const Main = () => 
+  <main>
+      <Aside />
+      <Content>
+          <Redirect from='/main' to='/'/>
+          <Switch>
+              <Route path='/' component={PageMain} exact />
+              <Route path="/category/:_id" component={CPageCategory} />
+              <Route path="/orderhistory" component={COrderHistiry} />
+              <Route path='/find/' component={CSearchResult} />
+              <Route path="/good/:_id" component={CPageGood} />
+              <Route path="/korzina" component={CCart} />
+              <Route path='*' component={Page404} />
+          </Switch>
+      </Content>
+  </main>
+
+export default Main;

+ 0 - 0
nix-project/src/components/Pages/Admin/Admin.js


+ 20 - 0
nix-project/src/components/Pages/Cart/Cart.js

@@ -0,0 +1,20 @@
+import React from "react";
+import { connect } from 'react-redux';
+import { actionCartClear } from "../../../actions/actionsCartReducer";
+import { actionNewOrder } from "../../../actions/GQLActions";
+import CGoodInCart from "../../GoodInCart/GoodInCart";
+
+const Cart = ({cart, onCartClear, onNewOrder}) => (
+    <div className='Cart'>
+      {cart.length === 0 ? <></> : <button onClick={() => onCartClear()}>Очистить корзину</button>} 
+      {cart.map(item => <CGoodInCart item={item} />)}
+      {cart.length === 0 ? <></> : <button onClick={() => {onNewOrder(); onCartClear()}}>Оформить заказ</button>} 
+    </div>
+  )
+  
+const CCart = connect(state => ({cart: Object.values(state.cart) || []}), 
+                                {   onCartClear: actionCartClear, 
+                                    onNewOrder: actionNewOrder
+                                })(Cart);
+
+export default CCart;                                

+ 9 - 0
nix-project/src/components/Pages/Content/Content.js

@@ -0,0 +1,9 @@
+import React from "react";
+
+const Content = ({children}) => (
+    <div className='Content'>
+        {children}
+    </div>
+)
+
+export default Content;

+ 8 - 0
nix-project/src/components/Pages/Page404/Page404.js

@@ -0,0 +1,8 @@
+import React from "react";
+
+const Page404 = () => 
+ <>
+    <h1>ОЙ...</h1>
+    <p>Мы не можем найти эту страницу 😢</p>
+ </>
+export default Page404;

+ 18 - 0
nix-project/src/components/Pages/PageCategory/PageCategory.js

@@ -0,0 +1,18 @@
+import React from "react";
+import { connect } from "react-redux";
+import { useEffect } from "react";
+import CCategory from "../../Category/Category";
+import { actionCatById } from "../../../actions/GQLActions";
+
+const PageCategory = ({match:{params:{_id}}, getData}) => {
+    useEffect(() => {
+        getData(_id)
+    },[_id])
+    return(
+        <CCategory />
+    )
+}
+
+const CPageCategory = connect(null, {getData: actionCatById})(PageCategory);
+
+export default CPageCategory;

+ 16 - 0
nix-project/src/components/Pages/PageGood/PageGood.js

@@ -0,0 +1,16 @@
+import React from "react";
+import { connect } from "react-redux";
+import { actionGoodById } from "../../../actions/GQLActions";
+import { useEffect } from "react";
+import CGood from "../../Good/Good";
+
+const PageGood = ({match: {params: { _id }}, getData}) => {
+    useEffect(() => {
+        getData(_id);
+    }, [_id]);
+    return <CGood />;
+  };
+  
+const CPageGood = connect(null, { getData: actionGoodById })(PageGood);
+
+export default CPageGood;

+ 6 - 0
nix-project/src/components/Pages/PageMain/PageMain.js

@@ -0,0 +1,6 @@
+import React from "react";
+
+const PageMain = () =>
+    <h1>Главная страница</h1>
+
+export default PageMain;

+ 61 - 0
nix-project/src/components/Pages/PageOrderHistory/OrderHistoryItem.js

@@ -0,0 +1,61 @@
+import React from "react";
+import { connect } from "react-redux";
+import { backURL } from "../../../services/getGQL";
+import { useEffect } from "react";
+import { actionGetOrdersHistory } from "../../../actions/GQLActions";
+
+const COrderHistoryGoodItem = ({ 
+    good: { 
+        price, 
+        total, 
+        count, 
+        good: { name, images, _id }, 
+        }, 
+    }) => { 
+    return ( 
+        <div> 
+        <h1>{name}</h1> 
+        {images && images[0] && images[0].url && ( 
+            <img src={backURL + "/" + images[0].url} /> 
+        )} 
+        <h1>{count}</h1> 
+        </div> 
+    ); 
+}; 
+
+const COrderHistoryItem = ({ order: { total, orderGoods } }) => { 
+    return ( 
+        <div> 
+            {orderGoods.map((good) => (<COrderHistoryGoodItem good={good} />))} 
+            <h3>{total}</h3> 
+        </div> 
+    ); 
+}; 
+
+const OrderHistiryItem = ({ history = [] }) => { 
+    return ( 
+        <> 
+            {history.map((order) => (<COrderHistoryItem order={order} />))} 
+        </> 
+    ); 
+}; 
+
+const COrderHistiryItem = connect((state) => ({history: state.promise.orderHistory?.payload,}))(OrderHistiryItem); 
+
+const OrderHistiry = ({ 
+    match: {params: { _id },}, 
+        getData, 
+    }) => { 
+        useEffect(() => { 
+        getData(_id); 
+    }, [_id]); 
+    return ( 
+        <> 
+            <COrderHistiryItem /> 
+        </> 
+); 
+}; 
+
+const COrderHistiry = connect(null, { getData: actionGetOrdersHistory })(OrderHistiry);
+
+export default COrderHistiry;

+ 26 - 0
nix-project/src/components/RegForm/RegForm.js

@@ -0,0 +1,26 @@
+import React from "react";
+import { connect } from "react-redux";
+import { useState } from "react";
+import { actionFullReg } from "../../actions/GQLActions";
+import createHistory from "history/createBrowserHistory";
+
+const history = createHistory();
+
+const RegForm = ({ onReg }) => {
+    const [login, setLogin] = useState("");
+    const [password, setPassword] = useState("");
+  
+    return (
+        <div className='link'>
+            {login === "" ? (<input style={{ border: "1px solid red" }} onChange={(e) => setLogin(e.target.value)}></input>)
+                      : (<input value={login} onChange={(e) => setLogin(e.target.value)} />)}
+            {password === "" ? (<input style={{ border: "1px solid red" }} onChange={(e) => setPassword(e.target.value)}></input>)
+                      : (<input value={password} onChange={(e) => setPassword(e.target.value)} />)}
+                         <button onClick={() => {onReg(login, password); history.push('/')}}>Register</button>
+        </div>
+    );
+  };
+  
+const CRegForm = connect(null,{onReg: actionFullReg})(RegForm)
+
+export default CRegForm;

+ 24 - 0
nix-project/src/components/RootCategories/RootCategories.js

@@ -0,0 +1,24 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import RootCategory from '../RootCategory/RootCategory';
+
+const defaultRootCats = [
+  {_id: '5dc49f4d5df9d670df48cc64', name: 'Airconditions'},
+  {_id: '5dc458985df9d670df48cc47', name: 'Smartphones'},
+  {_id: '5dc4b2553f23b553bf354101', name: 'Крупная бытовая техника'},
+  {_id: '5dcac1b56d09c45440d14cf8', name: 'Макароны'},
+  {_id: '5dcac6cf6d09c45440d14cfd', name: 'Drinks'},
+  {_id: '5dcacaeb6d09c45440d14d04', name: 'Салаты'},
+  {_id: '61715b92ef4e1b3e3b67703c', name: 'Приятный бонус'},
+  {_id: '5dc94bd00e36db246e3049ee', name: 'Пицца'},
+]
+
+const RootCategories = ({cats=defaultRootCats}) => (
+  <ul className='RootCategories'>
+    {cats.map(cat => <RootCategory cat = {cat} />)}
+  </ul>
+)
+
+const CRootCategories = connect(state => ({cats: state.promise.rootCats?.payload || []}))(RootCategories)
+
+export default CRootCategories;

+ 9 - 0
nix-project/src/components/RootCategory/RootCategory.js

@@ -0,0 +1,9 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+
+const RootCategory = ({cat: {_id, name}={}}) =>
+  <li>
+    <Link className='link' to={`/category/${_id}`}>{name}</Link>
+  </li>
+
+export default RootCategory;

+ 37 - 0
nix-project/src/components/Search/Search.js

@@ -0,0 +1,37 @@
+import React from "react";
+import { connect } from 'react-redux';
+import { useState } from "react";
+import { CGoodCard } from "../GoodCard/GoodCard";
+import { actionGoodFind } from "../../actions/GQLActions";
+import { Link } from "react-router-dom/cjs/react-router-dom.min";
+
+const Search = ({ onSearch }) => {
+  const [value, setValue] = useState('');
+
+  return (
+    <div>
+      <input type="text" placeholder='Поиск...' value={value} onChange={e => setValue(e.target.value)}/>
+      <Link to='/find'><button onClick={() => onSearch(value)}>Найти</button></Link>
+    </div>
+  )
+}
+
+const SearchResult = ({ goodFind }) => {
+
+  return (
+    <>
+      {goodFind.map(good => <CGoodCard good={good} />)}
+    </>
+  )
+}
+
+const CSearchResult = connect((state) => ({
+  goodFind: state.promise.goodFind?.payload || [],
+}))(SearchResult);
+
+const CSearch = connect(null, {onSearch: actionGoodFind})(Search);
+
+export {
+  CSearchResult,
+  CSearch
+}

+ 9 - 0
nix-project/src/components/SubCategories/SubCategories.js

@@ -0,0 +1,9 @@
+import React from "react";
+import RootCategory from "../RootCategory/RootCategory";
+
+const SubCategories = ({cats}) =>
+  <>
+    {cats.map(cat => <RootCategory cat={cat}/>)}
+  </>
+
+export default SubCategories;

BIN
nix-project/src/img/logo.png


+ 8 - 0
nix-project/src/index.js

@@ -0,0 +1,8 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import App from './App';
+
+ReactDOM.render(
+    <App />,
+  document.getElementById('root')
+);

+ 37 - 0
nix-project/src/reducers/authReducer.js

@@ -0,0 +1,37 @@
+function authReducer(state, { type, token }) {
+    if (!state) {
+        if (localStorage.authToken) {
+            type = 'AUTH_LOGIN'
+            token = localStorage.authToken
+        } else state = {}
+    }
+    if (type === 'AUTH_LOGIN') {
+        localStorage.setItem('authToken', token)
+        let payload = jwtDecode(token)
+        if (typeof payload === 'object') {
+            return {
+                ...state,
+                token,
+                payload
+            }
+        } else return state
+    }
+    if (type === 'AUTH_LOGOUT') {
+        localStorage.removeItem('authToken')
+        return {}
+    }
+    return state
+}
+
+const jwtDecode = token => {
+    try {
+        let arrToken = token.split('.')
+        let base64Token = atob(arrToken[1])
+        return JSON.parse(base64Token)
+    }
+    catch (e) {
+        console.log(e);
+    }
+}
+
+export default authReducer;

+ 42 - 0
nix-project/src/reducers/cartReducer.js

@@ -0,0 +1,42 @@
+function cartReducer(state = {}, { type, good = {}, count = 1 }) {
+    const { _id } = good
+    const types = {
+        CART_ADD() {
+            count = +count
+            if (!count) return state
+            return {
+                ...state,
+                [_id]: {
+                    good,
+                    count: count + (state[_id]?.count || 0)
+                }
+            }
+        },
+        CART_CHANGE() {
+            count = +count
+            if (!count) return state
+            return {
+                ...state,
+                [_id]: {
+                    good,
+                    count: count
+                }
+            }
+        },
+        CART_REMOVE() {
+            let { [_id]: remove, ...newState } = state
+            return {
+                ...newState
+            }
+        },
+        CART_CLEAR() {
+            return {}
+        },
+    }
+    if (type in types) {
+        return types[type]()
+    }
+    return state
+}
+
+export default cartReducer;

+ 11 - 0
nix-project/src/reducers/promiseReducer.js

@@ -0,0 +1,11 @@
+function promiseReducer(state = {}, { type, status, payload, error, name }) {
+    if (type === 'PROMISE') {
+        return {
+            ...state,
+            [name]: { status, payload, error }
+        }
+    }
+    return state;
+}
+
+export default promiseReducer;

+ 15 - 0
nix-project/src/reducers/store.js

@@ -0,0 +1,15 @@
+import thunk from 'redux-thunk';
+import promiseReducer from "./promiseReducer";
+import authReducer from "./authReducer";
+import cartReducer from "./cartReducer";
+import {createStore, combineReducers, applyMiddleware} from 'redux';
+import { actionRootCats, actionCatById } from '../actions/GQLActions';
+
+const store = createStore(combineReducers({ promise: promiseReducer,
+    auth: authReducer,
+    cart: cartReducer}), applyMiddleware(thunk))
+store.subscribe(() => console.log(store.getState()))  
+store.dispatch(actionRootCats());
+store.dispatch(actionCatById());
+
+export default store;

+ 20 - 0
nix-project/src/services/getGQL.js

@@ -0,0 +1,20 @@
+const getGQL = url =>
+    async (query, variables = {}) => {
+        let obj = await fetch(url, {
+            method: 'POST',
+            headers: {
+                "Content-Type": "application/json",
+                Authorization: localStorage.authToken ? 'Bearer ' + localStorage.authToken : {},
+            },
+            body: JSON.stringify({ query, variables })
+        })
+        let a = await obj.json()
+        if (!a.data && a.errors)
+            throw new Error(JSON.stringify(a.errors))
+        return a.data[Object.keys(a.data)[0]]
+}
+
+export const backURL = 'http://shop-roles.asmer.fs.a-level.com.ua';
+
+export const gql = getGQL(backURL + '/graphql');
+

+ 167 - 33
store/src/App.js

@@ -43,7 +43,7 @@ const jwtDecode = token => {
         return JSON.parse(base64Token)
     }
     catch (e) {
-        console.log('Лажа, Бро ' + e);
+        console.log(e);
     }
 }
 
@@ -177,7 +177,7 @@ actionPromise('rootCats', gql(`query {
   }
 }`))
 
-const actionGoodById = (_id) => actionPromise("goodById1", gql(
+const actionGoodById = (_id) => actionPromise("goodById", gql(
   `query goodById($q: String) {
  GoodFindOne(query: $q) {
   _id name description price categories {
@@ -193,6 +193,79 @@ const actionGoodById = (_id) => actionPromise("goodById1", gql(
   )
 );
 
+const actionGoodFind = (word) => (
+  actionPromise('goodFind', gql(`query goodById($q: String) {
+      GoodFind(query: $q) {
+          _id name price description images {
+          url
+          }
+      }
+  }`, {q: JSON.stringify([
+          {
+              $or: [{title: `/${word}/`}, {description: `/${word}/`}, {name: `/${word}/`}] 
+          },
+          {
+              sort: [{title: 1}]  
+          } 
+          ])
+      }
+  ))
+)
+
+const actionReg = (login, password) => {
+  return actionPromise(
+      "reg",
+      gql(
+          `mutation reg($l: String, $p: String){
+      UserUpsert(user: {login: $l,
+                        password:$p
+                      }){
+        _id login
+      }
+    }`,
+          { l: login, p: password }
+      )
+  );
+};
+
+const actionFullReg = (login, password) =>
+    async function a(dispatch) {
+        try {
+            await dispatch(actionReg(login, password));
+        } catch (e) {
+            return 0;
+        }
+        await dispatch(actionFullLogin(login, password));
+    }
+
+const actionNewOrder = () => async (dispatch, getState) => {
+  let { cart } = getState();
+  const orderGoods = Object.entries(cart).map(([_id, { count }]) => ({
+      good: { _id },
+      count,
+  }));
+
+  let result =  await dispatch(
+      actionPromise(
+          "order",gql(
+              `mutation newOrder($order:OrderInput){ 
+                            OrderUpsert(order:$order) 
+                            { _id total} 
+                          }`,
+              { order: {orderGoods} }) )
+  );
+};
+  
+const actionGetOrdersHistory = () => actionPromise("orderHistory", gql(`query o { 
+    OrderFind(query: "[{},{\\"sort\\": [{\\"_id\\": -1}]}]") { 
+      createdAt
+        orderGoods{
+            price, total, count
+            good{_id, name, images{_id, url}}
+        }, total
+    }
+  }`)
+)
 
 const store = createStore(combineReducers({ promise: promiseReducer,
                                             auth: authReducer,
@@ -203,7 +276,7 @@ store.dispatch(actionCatById());
 
 const defaultRootCats = [
   {_id: '5dc49f4d5df9d670df48cc64', name: 'Airconditions'},
-  {_id: '5dc458985df9d670df48cc47', name: '     Smartphones'},
+  {_id: '5dc458985df9d670df48cc47', name: 'Smartphones'},
   {_id: '5dc4b2553f23b553bf354101', name: 'Крупная бытовая техника'},
   {_id: '5dcac1b56d09c45440d14cf8', name: 'Макароны'},
   {_id: '5dcac6cf6d09c45440d14cfd', name: 'Drinks'},
@@ -219,7 +292,7 @@ const RootCategory = ({cat: {_id, name}={}}) =>
 
 const RootCategories = ({cats=defaultRootCats}) => (
   <ul className='RootCategories'>
-    {cats.map(cat => <RootCategory cat ={cat} />)}
+    {cats.map(cat => <RootCategory cat = {cat} />)}
   </ul>
 )
 
@@ -227,18 +300,36 @@ const CRootCategories = connect(state => ({cats: state.promise.rootCats?.payload
 
 const GoodCard = ({good: {_id, name, price, images}={}, onCartAdd}) =>
   <div className='GoodCard'>
-    <Link to={`/good/${_id}`} className="Good"><h2>{name}</h2></Link>
+    <Link to={`/good/${_id}`}><h2>{name}</h2></Link>
     {images && images[0] && images[0].url && <img src={backURL + '/' + images[0].url} />}
-    <strong>{price}</strong>
+    <strong>{price}</strong>
     <button onClick={() => onCartAdd({_id, name, price, images})}>+</button>
   </div>
 
-const CGoodCard = connect(null, {onCartAdd: actionCartAdd})(GoodCard)
+const CGoodCard = connect(null, {onCartAdd: actionCartAdd})(GoodCard);
+
+const Search = ({ onSearch }) => {
+  const [value, setValue] = useState('');
+
+  return (
+    <div>
+      <input type="text" placeholder='Поиск...' value={value} onChange={e => setValue(e.target.value)}/>
+      <button onClick={() => onSearch(value)}>Найти</button>
+    </div>
+  )
+}
+
+const CSearch = connect(null, {onSearch: actionGoodFind})(Search);
+
+const SubCategories = ({cats}) =>
+  <>
+    {cats.map(cat => <RootCategory cat={cat}/>)}
+  </>
 
-const Category = ({cat: {_id, name, goods, subCategories}=actionCatById}) => 
+const Category = ({cat: {name, goods, subCategories}}) => 
   <div className='Category'>
     <h1>{name}</h1>
-    {subCategories && <subCategories cats={subCategories} />}
+    {subCategories && <SubCategories cats={subCategories} />}
     {(goods || []).map(good => <CGoodCard good={good} />)}
   </div>
 
@@ -259,7 +350,7 @@ const CKoshik = connect(({cart}) => ({cart}))(Koshik)
 const GoodInCart =  ({item: {count, good: {_id, name, price, images}}, onCartChange, onCartRemove}) => (
   <div className='GoodInCart'>
       <h2>{name}</h2>
-      <h2>{count}</h2>
+      <strong>{count}</strong> шт. на <strong>{price*count} ₴</strong>
       {images && images[0] && images[0].url && <img src={backURL + '/' + images[0].url} />}
       <strong>{price}</strong>
       <br/>
@@ -269,22 +360,24 @@ const GoodInCart =  ({item: {count, good: {_id, name, price, images}}, onCartCha
   </div>
 )
 
-const Cart = ({cart, onCartClear}) => (
+const CGoodInCart = connect(null, { onCartChange: actionCartChange, 
+                                    onCartRemove: actionCartRemove, 
+                                    onCartAdd: actionCartAdd
+                                  })(GoodInCart)
+
+const Cart = ({cart, onCartClear, onNewOrder}) => (
   <div className='Cart'>
     {cart.length === 0 ? <></> : <button onClick={() => onCartClear()}>Очистить корзину</button>} 
     {cart.map(item => <CGoodInCart item={item} />)}
-    {cart.length === 0 ? <></> : <button>Оформить заказ</button>} 
+    {cart.length === 0 ? <></> : <button onClick={() => {onNewOrder(); onCartClear()}}>Оформить заказ</button>} 
   </div>
 )
 
 const CCart = connect(state => ({cart: Object.values(state.cart) || []}), 
-                                { onCartClear: actionCartClear})(Cart);
+                                { onCartClear: actionCartClear, onNewOrder: actionNewOrder})(Cart);
 
-const CGoodInCart = connect(null, { onCartChange: actionCartChange, 
-                                    onCartRemove: actionCartRemove, 
-                                    onCartAdd: actionCartAdd
-                                  })(GoodInCart)
 
+  
 // const LowerCase = ({children}) => (
 //   <>{children.toLowerCase()}</>
 // )
@@ -372,6 +465,26 @@ const CGoodInCart = connect(null, { onCartChange: actionCartChange,
 //     </>
 //   )
 // }
+const LoginButtons = ({onLogout, history, token}) => {
+
+  return (
+      <>
+          {!token ?
+              ( <div className='loginButtons'>
+                  <Link to='/login'><button className='btn' onClick={() => history.push('/')}>Вход</button></Link>
+                  <Link to='/registration'><button className='btn'>Регистрация</button></Link>
+              </div>) :
+              (<div className='LoginButtons'>
+                <strong>{`Привет, ${JSON.parse(atob(token.split(".")[1])).sub.login}!`}</strong>
+                <Link to='/orderhistory'><button className='exit'>История заказов</button></Link>
+                <button className='exit' onClick={() => {onLogout(); history.push('/')}}>Выйти</button>
+              </div>)
+          }
+      </>
+  )
+}
+
+const CLoginButtons = connect(state => ({token: state.auth.token}), {onLogout: actionAuthLogout})(LoginButtons)
 
 const LoginForm = ({onLogin}) => {
 
@@ -382,13 +495,30 @@ const LoginForm = ({onLogin}) => {
     <div className='LoginForm'>
       <input type="text" value={login} onChange={e => setLogin(e.target.value)} style={{border: (login.length === 0) ? '1px solid red' : ''}}/>
       <input type="password" value={password} onChange={e => setPassword(e.target.value)} style={{border: (password.length === 0) ? '1px solid red' : ''}}/>
-      <button className='loginBtn' onClick={() => onLogin(login,password)} disabled={login.length == 0 || password.length == 0}>LOGIN</button>
+      <button className='link' onClick={() => onLogin(login,password)} disabled={login.length == 0 || password.length == 0}>LOGIN</button>
     </div>
   )
 }
 
 const CLoginForm = connect(null, {onLogin: actionFullLogin})(LoginForm)
 
+const RegForm = ({ onReg }) => {
+  const [login, setLogin] = useState("");
+  const [password, setPassword] = useState("");
+
+  return (
+      <div className='link'>
+          {login === "" ? (<input style={{ border: "1px solid red" }} onChange={(e) => setLogin(e.target.value)}></input>)
+                    : (<input value={login} onChange={(e) => setLogin(e.target.value)} />)}
+          {password === "" ? (<input style={{ border: "1px solid red" }} onChange={(e) => setPassword(e.target.value)}></input>)
+                    : (<input value={password} onChange={(e) => setPassword(e.target.value)} />)}
+                       <button onClick={() => {onReg(login, password); history.push('/')}}>Register</button>
+      </div>
+  );
+};
+
+const CRegForm = connect(null,{onReg: actionFullReg})(RegForm)
+
 const Logo = ({logo}) => (
   <a href='#' className="Logo">
       <img src={logo} />
@@ -400,6 +530,12 @@ const Header = ({logo=logoDefault}) => (
       <h1 className='name'>S T O R E</h1>
       <Logo logo={logo} />
       <CKoshik />
+      <CSearch />
+      <Switch>
+        <Route path="/login" component={CLoginForm}/>
+        <Route path="/registration" component={CRegForm}/>
+        <Route path='*' component={CLoginButtons} />
+      </Switch>
   </header>
 )
 
@@ -447,7 +583,6 @@ const PageGood = ({match: {params: { _id }}, getData}) => {
       getData(_id);
   }, [_id]);
   return <CGood />;
-  //   return <h1>{console.log(_id)}</h1>;
 };
 
 const CPageGood = connect(null, { getData: actionGoodById })(PageGood);
@@ -455,19 +590,19 @@ const CPageGood = connect(null, { getData: actionGoodById })(PageGood);
 const Page404 = () => <h1>PAGE НЭМА</h1>
 
 const Main = () => 
-<main>
-    <Aside />
-    <Content>
-        <Redirect from='/main' to='/'/>
-        <Switch>
-            <Route path='/' component={PageMain} exact/>
-            <Route path="/category/:_id" component={CPageCategory}/>
-            <Route path="/good/:_id" component={CPageGood}/>
-            <Route path="/korzina" component={CCart}/>
-            <Route path='*' component={Page404} />
-        </Switch>
-    </Content>
-</main>
+  <main>
+      <Aside />
+      <Content>
+          <Redirect from='/main' to='/'/>
+          <Switch>
+              <Route path='/' component={PageMain} exact/>
+              <Route path="/category/:_id" component={CPageCategory}/>
+              <Route path="/good/:_id" component={CPageGood}/>
+              <Route path="/korzina" component={CCart}/>
+              <Route path='*' component={Page404} />
+          </Switch>
+      </Content>
+  </main>
 
 
 const Footer = () => (
@@ -502,7 +637,6 @@ function App() {
     <Router history={history}>
       <Provider store={store}> 
         <div className="App">
-          <CLoginForm onLogin={(l,p) => console.log(l,p)} />
           {/* <RGBInput /> */}
           {/* <Input /> */}
           {/* {list} */}

+ 40 - 0
store/src/App.scss

@@ -79,4 +79,44 @@ background-color: black;
         background-color: rgb(255, 255, 255);
         color: black;
     }
+}
+
+.loginButtons {
+    display: flex;
+    flex-direction: column;
+
+    .btn {
+        width: 100%;
+        background-color: black; 
+        border: none;
+        color: white;
+        padding: 15px 32px;
+        text-align: center;
+        text-decoration: none;
+        display: inline-block;
+        font-size: 16px;
+
+        &:hover {
+            background-color: white; 
+            color: black;
+        }
+    }
+}
+
+.exit {
+    display: block;
+    width: 100%;
+    background-color: black; 
+    border: none;
+    color: white;
+    padding: 15px 32px;
+    text-align: center;
+    text-decoration: none;
+    display: inline-block;
+    font-size: 16px;
+
+    &:hover {
+        background-color: white; 
+        color: black;
+    }
 }