Vitalii Polishchuk 3 vuotta sitten
vanhempi
commit
122fdd81dc

+ 206 - 0
js/18-js-redux-combine(cart)/css/main.css

@@ -0,0 +1,206 @@
+.main-container {
+    display: flex;
+    width: 100%;
+    height: 100vh;
+}
+
+#aside {
+    display: flex;
+    flex-direction: column;
+    background-color: #eb0000;
+    padding: 10px;
+    width: 15%;
+    position: fixed;
+}
+
+#aside a {
+    text-decoration: none;
+    color: white;
+    margin: 10px 0;
+    transition: all 0.5s;
+}
+
+#main {
+    display: flex;
+    flex-direction: column;
+    width: 85%;
+    margin-left: 20%;
+}
+
+#main h2 {
+    margin-left: 20px;
+}
+
+.cards {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: flex-start;
+    width: 100%;
+    padding: 20px;
+    text-align: center;
+}
+
+.cards a {
+    text-decoration: none;
+    color: #000;
+    margin: 15px;
+    border-radius: 10px;
+    width: 25%;
+    transition: all 0.5s;
+    box-shadow: 0 0 2px rgb(8, 8, 8);
+}
+
+.cards a:hover {
+    transform: scale(1.05);
+}
+
+.card img {
+    max-width: 80%;
+    margin: 10px 0;
+}
+
+.card {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: space-between;
+    height: 100%;
+    padding: 15px;
+}
+
+button {
+    box-sizing: border-box;
+    background-color: transparent;
+    border: 2px solid #e74c3c;
+    border-radius: 0.6em;
+    color: #e74c3c;
+    cursor: pointer;
+    line-height: 1;
+    margin: 10px;
+    padding: 0.5em;
+    text-decoration: none;
+    text-align: center;
+    text-transform: uppercase;
+    transition: box-shadow 0.3s ease-in-out, color 0.3s ease-in-out;
+}
+
+button:hover {
+    color: #fff;
+    outline: 0;
+    box-shadow: 0 0 40px 40px #e74c3c inset;
+}
+
+input {
+    width: 40px;
+    height: 35px;
+    text-align: center;
+    border: 2px solid #e74c3c;
+    border-radius: 0.6em;
+}
+
+#cart {
+    position: absolute;
+    top: 10px;
+    right: 10%;
+}
+
+#cart span {
+    position: absolute;
+    top: 0;
+    right: -5px;
+    border: 1px solid #000;
+    background-color: #fff;
+    border-radius: 10px;
+    padding: 0 4px;
+    font-size: 12px;
+    text-align: center;
+}
+
+.cart-item {
+    display: flex;
+    align-items: center;
+    font-size: 18px;
+    border-bottom: 1px solid #8d8d8d;
+}
+
+.btn-plus,
+.btn-minus,
+.btn-del {
+    position: relative;
+    width: 25px;
+    height: 25px;
+    border-radius: 55%;
+    border: none;
+}
+
+.btn-plus::before,
+.btn-plus::after,
+.btn-minus::before,
+.btn-del::before,
+.btn-del::after {
+    content: "";
+    display: block;
+    position: absolute;
+    width: 2px;
+    height: 12px;
+    background-color: #e74c3c;
+    top: 50%;
+    left: 50%;
+    margin-right: -50%;
+    transition: background-color 0.3s;
+}
+
+.btn-plus::before,
+.btn-minus::before {
+    transform: translate(-50%, -50%) rotate(90deg);
+}
+
+.btn-plus::after {
+    transform: translate(-50%, -50%);
+}
+
+.btn-del::before {
+    transform: translate(-50%, -50%) rotate(45deg);
+}
+
+.btn-del::after {
+    transform: translate(-50%, -50%) rotate(-45deg);
+}
+
+.btn-plus:hover::after,
+.btn-plus:hover::before,
+.btn-minus:hover::before,
+.btn-del:hover::after,
+.btn-del:hover::before {
+    background-color: #fff;
+}
+
+.btn-plus:active,
+.btn-minus:active,
+.btn-del:active {
+    transform: scale(0.95);
+}
+
+.btn-minus:disabled {
+    border-color: grey;
+    cursor: default;
+    box-shadow: none;
+}
+.btn-minus:disabled::before {
+    background-color: grey;
+}
+
+.cart-item-count {
+    color: #e74c3c;
+    width: 30px;
+    height: 30px;
+}
+
+.btn-clear {
+    color: #706e6e;
+    border-color: #706e6e;
+}
+
+.btn-clear:hover {
+    box-shadow: 0 0 40px 40px #706e6e inset;
+}

+ 357 - 0
js/18-js-redux-combine(cart)/css/normalize.css

@@ -0,0 +1,357 @@
+*,
+*::before,
+*::after {
+    box-sizing: border-box;
+}
+
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
+
+/* Document
+   ========================================================================== */
+
+/**
+ * 1. Correct the line height in all browsers.
+ * 2. Prevent adjustments of font size after orientation changes in iOS.
+ */
+
+html {
+    line-height: 1.15; /* 1 */
+    -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/* Sections
+     ========================================================================== */
+
+/**
+   * Remove the margin in all browsers.
+   */
+
+body {
+    margin: 0;
+}
+
+/**
+   * Render the `main` element consistently in IE.
+   */
+
+main {
+    display: block;
+}
+
+/**
+   * Correct the font size and margin on `h1` elements within `section` and
+   * `article` contexts in Chrome, Firefox, and Safari.
+   */
+
+h1 {
+    font-size: 2em;
+    margin: 0.67em 0;
+}
+
+/* Grouping content
+     ========================================================================== */
+
+/**
+   * 1. Add the correct box sizing in Firefox.
+   * 2. Show the overflow in Edge and IE.
+   */
+
+hr {
+    box-sizing: content-box; /* 1 */
+    height: 0; /* 1 */
+    overflow: visible; /* 2 */
+}
+
+/**
+   * 1. Correct the inheritance and scaling of font size in all browsers.
+   * 2. Correct the odd `em` font sizing in all browsers.
+   */
+
+pre {
+    font-family: monospace, monospace; /* 1 */
+    font-size: 1em; /* 2 */
+}
+
+/* Text-level semantics
+     ========================================================================== */
+
+/**
+   * Remove the gray background on active links in IE 10.
+   */
+
+a {
+    background-color: transparent;
+}
+
+/**
+   * 1. Remove the bottom border in Chrome 57-
+   * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+   */
+
+abbr[title] {
+    border-bottom: none; /* 1 */
+    text-decoration: underline; /* 2 */
+    text-decoration: underline dotted; /* 2 */
+}
+
+/**
+   * Add the correct font weight in Chrome, Edge, and Safari.
+   */
+
+b,
+strong {
+    font-weight: bolder;
+}
+
+/**
+   * 1. Correct the inheritance and scaling of font size in all browsers.
+   * 2. Correct the odd `em` font sizing in all browsers.
+   */
+
+code,
+kbd,
+samp {
+    font-family: monospace, monospace; /* 1 */
+    font-size: 1em; /* 2 */
+}
+
+/**
+   * Add the correct font size in all browsers.
+   */
+
+small {
+    font-size: 80%;
+}
+
+/**
+   * Prevent `sub` and `sup` elements from affecting the line height in
+   * all browsers.
+   */
+
+sub,
+sup {
+    font-size: 75%;
+    line-height: 0;
+    position: relative;
+    vertical-align: baseline;
+}
+
+sub {
+    bottom: -0.25em;
+}
+
+sup {
+    top: -0.5em;
+}
+
+/* Embedded content
+     ========================================================================== */
+
+/**
+   * Remove the border on images inside links in IE 10.
+   */
+
+img {
+    border-style: none;
+}
+
+/* Forms
+     ========================================================================== */
+
+/**
+   * 1. Change the font styles in all browsers.
+   * 2. Remove the margin in Firefox and Safari.
+   */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+    font-family: inherit; /* 1 */
+    font-size: 100%; /* 1 */
+    line-height: 1.15; /* 1 */
+    margin: 0; /* 2 */
+}
+
+/**
+   * Show the overflow in IE.
+   * 1. Show the overflow in Edge.
+   */
+
+button,
+input {
+    /* 1 */
+    overflow: visible;
+}
+
+/**
+   * Remove the inheritance of text transform in Edge, Firefox, and IE.
+   * 1. Remove the inheritance of text transform in Firefox.
+   */
+
+button,
+select {
+    /* 1 */
+    text-transform: none;
+}
+
+/**
+   * Correct the inability to style clickable types in iOS and Safari.
+   */
+
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+    -webkit-appearance: button;
+}
+
+/**
+   * Remove the inner border and padding in Firefox.
+   */
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+    border-style: none;
+    padding: 0;
+}
+
+/**
+   * Restore the focus styles unset by the previous rule.
+   */
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+    outline: 1px dotted ButtonText;
+}
+
+/**
+   * Correct the padding in Firefox.
+   */
+
+fieldset {
+    padding: 0.35em 0.75em 0.625em;
+}
+
+/**
+   * 1. Correct the text wrapping in Edge and IE.
+   * 2. Correct the color inheritance from `fieldset` elements in IE.
+   * 3. Remove the padding so developers are not caught out when they zero out
+   *    `fieldset` elements in all browsers.
+   */
+
+legend {
+    box-sizing: border-box; /* 1 */
+    color: inherit; /* 2 */
+    display: table; /* 1 */
+    max-width: 100%; /* 1 */
+    padding: 0; /* 3 */
+    white-space: normal; /* 1 */
+}
+
+/**
+   * Add the correct vertical alignment in Chrome, Firefox, and Opera.
+   */
+
+progress {
+    vertical-align: baseline;
+}
+
+/**
+   * Remove the default vertical scrollbar in IE 10+.
+   */
+
+textarea {
+    overflow: auto;
+}
+
+/**
+   * 1. Add the correct box sizing in IE 10.
+   * 2. Remove the padding in IE 10.
+   */
+
+[type="checkbox"],
+[type="radio"] {
+    box-sizing: border-box; /* 1 */
+    padding: 0; /* 2 */
+}
+
+/**
+   * Correct the cursor style of increment and decrement buttons in Chrome.
+   */
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+    height: auto;
+}
+
+/**
+   * 1. Correct the odd appearance in Chrome and Safari.
+   * 2. Correct the outline style in Safari.
+   */
+
+[type="search"] {
+    -webkit-appearance: textfield; /* 1 */
+    outline-offset: -2px; /* 2 */
+}
+
+/**
+   * Remove the inner padding in Chrome and Safari on macOS.
+   */
+
+[type="search"]::-webkit-search-decoration {
+    -webkit-appearance: none;
+}
+
+/**
+   * 1. Correct the inability to style clickable types in iOS and Safari.
+   * 2. Change font properties to `inherit` in Safari.
+   */
+
+::-webkit-file-upload-button {
+    -webkit-appearance: button; /* 1 */
+    font: inherit; /* 2 */
+}
+
+/* Interactive
+     ========================================================================== */
+
+/*
+   * Add the correct display in Edge, IE 10+, and Firefox.
+   */
+
+details {
+    display: block;
+}
+
+/*
+   * Add the correct display in all browsers.
+   */
+
+summary {
+    display: list-item;
+}
+
+/* Misc
+     ========================================================================== */
+
+/**
+   * Add the correct display in IE 10+.
+   */
+
+template {
+    display: none;
+}
+
+/**
+   * Add the correct display in IE 10.
+   */
+
+[hidden] {
+    display: none;
+}

+ 5 - 0
js/18-js-redux-combine(cart)/img/cart.svg

@@ -0,0 +1,5 @@
+<svg width="32px" height="32px" viewBox="0 0 32 32" id="i-cart" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+    <path d="M6 6 L30 6 27 19 9 19 M27 23 L10 23 5 2 2 2" />
+    <circle cx="25" cy="27" r="2" />
+    <circle cx="12" cy="27" r="2" />
+</svg>

+ 25 - 0
js/18-js-redux-combine(cart)/index.html

@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Document</title>
+    <link rel="stylesheet" href="css/normalize.css">
+    <link rel="stylesheet" href="css/main.css">
+</head>
+
+<body>
+    <div class="main-container">
+        <aside id="aside">Категории</aside>
+        <main id="main"></main>
+        <a id="cart" href="#/cart">
+            <img src="img/cart.svg" alt="cart">
+        </a>
+    </div>
+
+    <script src="js/main.js"></script>
+</body>
+
+</html>

+ 429 - 0
js/18-js-redux-combine(cart)/js/main.js

@@ -0,0 +1,429 @@
+function createStore(reducer) {
+    let state = reducer(undefined, {})
+    let cbs = []
+    function dispatch(action) {
+        if (typeof action === 'function') {
+            return action(dispatch)
+        }
+        const newState = reducer(state, action)
+        if (state !== newState) {
+            state = newState
+            cbs.forEach(cb => cb())
+        }
+    }
+    return {
+        dispatch,
+        subscribe(cb) {
+            cbs.push(cb)
+            return () => cbs = cbs.filter(c => c !== cb)
+        },
+        getState() {
+            return state
+        }
+    }
+}
+
+//reducers
+function promiseReducer(state = {}, { type, status, payload, error, name }) {
+    if (type === 'PROMISE') {
+        return {
+            ...state,
+            [name]: { status, payload, error }
+        }
+    }
+    return state
+}
+
+function cartReducer(state = {}, { type, count = 1, _id, name }) {
+    if (type === "CART_ADD") {
+        return {
+            ...state,
+            [_id]: {
+                name: name,
+                count: state[_id] ? state[_id].count + count : count
+            }
+        }
+    }
+    if (type === "CART_CHANGE") {
+        return {
+            ...state,
+            [_id]: {
+                name: name,
+                count: count
+            }
+        }
+    }
+    if (type === "CART_REMOVE") {
+        let { [_id]: a, ...rest } = state
+        return rest
+    }
+    if (type === "CART_CLEAR") {
+        return {}
+    }
+    return state
+}
+
+let reducers = {
+    promise: promiseReducer,
+    cart: cartReducer
+}
+
+function combineReducers(reducers) {
+    function commonReducer(state = {}, action) {
+        let newState = {}
+
+        for (let key in reducers) {
+            let innerState = reducers[key](state[key], action)
+
+            innerState === state[key] ? newState[key] = state[key] : newState[key] = innerState
+        }
+
+        return newState
+    }
+
+    return commonReducer
+}
+
+const store = createStore(combineReducers(reducers))
+
+//запросики
+const getGQL = url => {
+    return (query, variables) => {
+        return fetch(url, {
+            method: 'POST',
+            headers: {
+                "content-type": "application/json",
+                ...(localStorage.authToken ? { Authorization: "Bearer " + localStorage.authToken } : {})
+            },
+            body: JSON.stringify({ query, variables }),
+        }).then(res => res.json())
+    }
+}
+
+let shopGQL = getGQL('http://shop-roles.asmer.fs.a-level.com.ua/graphql')
+
+let categoryById = async (id) => {
+    let query = `query fndcategory($id: String) {
+        CategoryFind(query: $id){
+          name goods{
+              _id name price images {
+                  url
+              }
+          }
+        }
+      }`
+
+    let qVariables = {
+        "id": JSON.stringify([{ "_id": id }])
+    }
+
+    let res = await shopGQL(query, qVariables)
+    console.log(res)
+    return res
+}
+
+let goodById = async (id) => {
+    let query = `query fndgood($id: String) {
+        GoodFind(query: $id){
+          name description price images {
+              url
+          }
+        }
+      }`
+
+    let qVariables = {
+        "id": JSON.stringify([{ "_id": id }])
+    }
+
+    let res = await shopGQL(query, qVariables)
+    return res
+}
+
+let newOrder = async (obj) => {
+    let option = Object.entries(obj);
+    let orderGoods = [];
+
+    for (let key of option) {
+        let i = {
+            "count": key[1],
+            "good": { "_id": key[0] }
+        }
+        orderGoods.push(i);
+    }
+
+    let query = `mutation sndOrder($order: OrderInput) {
+       OrderUpsert(order: $order) {
+         _id createdAt total
+       }
+     }`
+
+    let qVariables = {
+        "order": {
+            "orderGoods": orderGoods
+        }
+    }
+
+    let res = await shopGQL(query, qVariables)
+    console.log(res)
+    return res
+}
+
+//actions
+const actionPending = name => ({ type: 'PROMISE', status: 'PENDING', name })
+const actionResolved = (name, payload) => ({ type: 'PROMISE', status: 'RESOLVED', name, payload })
+const actionRejected = (name, error) => ({ type: 'PROMISE', status: 'REJECTED', name, error })
+
+const actionPromise = (name, promise) =>
+    async dispatch => {
+        dispatch(actionPending(name))
+        try {
+            let payload = await promise
+            dispatch(actionResolved(name, payload))
+            return payload
+        }
+        catch (error) {
+            dispatch(actionRejected(name, error))
+        }
+    }
+
+const actionRootCategories = () =>
+    actionPromise('rootCategories', shopGQL(`
+        query cats($query:String){
+          CategoryFind(query:$query){
+            _id name 
+          }
+        }
+    `, { query: JSON.stringify([{ parent: null }]) }))
+
+const actionCategoryById = id => actionPromise('catById', categoryById(id))
+
+const actionGoodById = id => actionPromise('goodById', goodById(id))
+
+const actionBuyGood = (obj) => actionPromise('newOrder', newOrder(obj))
+
+const actionCartAdd = (n, id, name) => ({ type: "CART_ADD", count: n, _id: id, name })
+
+const actionCartChange = (n, id, name) => ({ type: "CART_CHANGE", count: n, _id: id, name })
+
+const actionCartRemove = id => ({ type: "CART_REMOVE", _id: id })
+
+const actionCartClear = () => ({ type: "CART_CLEAR" })
+
+store.dispatch(actionRootCategories())
+
+window.onhashchange = () => {
+    let { 1: route, 2: id } = location.hash.split('/')
+    if (route === 'categories') {
+        store.dispatch(actionCategoryById(id))
+    }
+
+    if (route === 'good') {
+        store.dispatch(actionGoodById(id))
+    }
+
+    if (route === 'cart') {
+        cartDraw(store.getState().cart, main)
+    }
+}
+
+//рисовашки
+function drawMainMenu() {
+    let cats = store.getState().promise.rootCategories.payload
+    if (cats) { //каждый раз дорисовываются в body
+        aside.innerText = ''
+        for (let { _id, name } of cats.data.CategoryFind) {
+            let catA = document.createElement('a')
+            catA.href = `#/categories/${_id}`
+            catA.innerText = name
+            aside.append(catA)
+        }
+    }
+}
+
+let goodDraw = (obj, parent, _id) => {
+    let box = document.createElement("div")
+    let goodName = document.createElement("h2")
+    let goodIMG = document.createElement("img")
+    let description = document.createElement("p")
+    let price = document.createElement("span")
+    let count = document.createElement("input")
+    let goodBtnBox = document.createElement("div")
+    let btn1 = document.createElement("button")
+    let btn2 = document.createElement("button")
+    price.textContent = "Цена: " + obj.price + "грн"
+    goodIMG.src = "http://shop-roles.asmer.fs.a-level.com.ua/" + obj.images[0].url
+    box.style.width = "40%"
+    box.style.margin = "20px"
+    box.style.display = "flex"
+    box.style.flexDirection = "column"
+    goodName.textContent = obj.name
+    description.textContent = obj.description
+    count.type = "number"
+    count.min = 1
+    count.value = 1
+    goodBtnBox.style.display = "flex"
+    goodBtnBox.style.alignItems = "center"
+    btn1.textContent = "Купить"
+    btn2.textContent = "В корзину"
+    goodBtnBox.append(count, btn2, btn1)
+
+    let order = {
+        [_id]: +count.value
+    }
+
+    count.oninput = () => order[_id] = +count.value
+
+    btn1.onclick = () => {
+        store.dispatch(actionBuyGood(order))
+    }
+    btn2.onclick = () => {
+        store.dispatch(actionCartAdd(+count.value, _id, obj.name))
+    }
+
+    box.append(goodIMG, goodName, description, price, goodBtnBox)
+    parent.append(box)
+}
+
+let cardDraw = (obj, parent) => {
+    let box = document.createElement("div");
+    let img = document.createElement("img");
+    let price = document.createElement("span");
+    let productName = document.createElement("h5");
+    img.src = "http://shop-roles.asmer.fs.a-level.com.ua/" + obj.images[0].url;
+    productName.textContent = obj.name
+    price.textContent = "Цена: " + obj.price + " грн"
+    let productBody = document.createElement("div")
+    box.className = "card"
+    productBody.append(productName, price)
+    box.append(img, productBody)
+    let a = document.createElement("a")
+    a.href = "#/good/" + obj._id
+    a.append(box)
+    parent.append(a)
+}
+
+let cartDraw = (obj, parent) => {
+    while (parent.lastChild) {
+        parent.lastChild.remove()
+    }
+    let cartName = document.createElement("h2")
+    cartName.textContent = "Ваш заказ"
+    parent.append(cartName)
+
+    let order = {}
+
+    for (let key in obj) {
+        order[key] = obj[key].count
+        let cartItem = document.createElement("div")
+        let name = document.createElement("span")
+        let count = document.createElement("input")
+        let btnDelItem = document.createElement("button")
+        let btnPlus = document.createElement("button")
+        let btnMinus = document.createElement("button")
+        count.min = 1
+        count.value = obj[key].count
+        count.className = "cart-item-count"
+        btnPlus.className = "btn-plus"
+        btnMinus.className = "btn-minus"
+        btnDelItem.className = "btn-del"
+        cartItem.className = "cart-item"
+        name.textContent = obj[key].name
+        cartItem.append(btnMinus, count, btnPlus, name, btnDelItem)
+        parent.append(cartItem)
+
+        if (+count.value === 1) {
+            btnMinus.disabled = "true"
+        }
+
+        count.oninput = () => {
+            if (+count.value >= 1) {
+                store.dispatch(actionCartChange(+count.value, key, obj[key].name))
+            }
+        }
+
+        btnPlus.onclick = () => {
+            store.dispatch(actionCartChange(+count.value + 1, key, obj[key].name))
+        }
+
+        btnMinus.onclick = () => {
+            store.dispatch(actionCartChange(+count.value - 1, key, obj[key].name))
+        }
+
+        btnDelItem.onclick = () => {
+            store.dispatch(actionCartRemove(key))
+        }
+    }
+
+    let btnBox = document.createElement("div")
+    let btnOrder = document.createElement("button")
+    let btnClear = document.createElement("button")
+    btnClear.textContent = "ОЧИСТИТЬ КОРЗИНУ"
+    btnClear.className = "btn-clear"
+    btnOrder.textContent = "КУПИТЬ"
+
+    btnOrder.onclick = () => {
+        store.dispatch(actionBuyGood(order))
+        store.dispatch(actionCartClear())
+    }
+
+    btnClear.onclick = () => {
+        store.dispatch(actionCartClear())
+    }
+
+    btnBox.append(btnOrder, btnClear)
+    parent.append(btnBox)
+}
+
+let cartValueDraw = () => {
+    if (cart.children.length === 1) {
+        let span = document.createElement("span")
+        cart.append(span)
+    }
+
+    let sum = 0;
+    let cartState = store.getState().cart
+    for (let key in cartState) {
+        sum += cartState[key].count
+    }
+
+    cart.lastChild.textContent = sum
+}
+
+//subscribers
+const unsubscribe1 = store.subscribe(() => console.log(store.getState()))
+store.subscribe(drawMainMenu)
+store.subscribe(cartValueDraw)
+
+store.subscribe(() => {
+    const { 1: route, 2: id } = location.hash.split('/')
+    if (route === 'categories') {
+        const catById = store.getState().promise.catById?.payload
+        if (catById) {
+            while (main.lastChild) {
+                main.lastChild.remove()
+            }
+
+            let categoryName = document.createElement("h2")
+            let cards = document.createElement("div")
+            cards.className = "cards"
+            categoryName.textContent = catById.data.CategoryFind[0].name
+
+            for (let key of catById.data.CategoryFind[0].goods) {
+                cardDraw(key, cards)
+            }
+            main.append(categoryName, cards)
+        }
+    }
+    if (route === 'good') {
+        const goodById = store.getState().promise.goodById?.payload
+        if (goodById) { //вывести в main страницу товара
+            while (main.lastChild) {
+                main.lastChild.remove()
+            }
+            goodDraw(goodById.data.GoodFind[0], main, id)
+        }
+    }
+    if (route === 'cart') {
+        cartDraw(store.getState().cart, main)
+    }
+})