Vitalii Polishchuk vor 3 Jahren
Ursprung
Commit
48d92f1bf1

+ 99 - 0
js/17-js-redax-promise/css/main.css

@@ -0,0 +1,99 @@
+.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;
+}

+ 357 - 0
js/17-js-redax-promise/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;
+}

+ 21 - 0
js/17-js-redax-promise/index.html

@@ -0,0 +1,21 @@
+<!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>
+    </div>
+    <script src="js/main.js"></script>
+</body>
+
+</html>

+ 280 - 0
js/17-js-redax-promise/js/main.js

@@ -0,0 +1,280 @@
+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
+        }
+    }
+}
+function promiseReducer(state = {}, { type, status, payload, error, name }) {
+    if (type === 'PROMISE') {
+        return {
+            ...state,
+            [name]: { status, payload, error }
+        }
+    }
+    return state
+}
+
+
+const store = createStore(promiseReducer)
+const unsubscribe1 = store.subscribe(() => console.log(store.getState()))
+
+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 delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
+
+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 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
+}
+
+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 actionGoodOrder = (obj) => actionPromise('newOrderGood', newOrderGood(obj))
+
+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))
+    }
+}
+
+function drawMainMenu() {
+    let cats = store.getState().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)
+        }
+    }
+}
+
+store.subscribe(drawMainMenu)
+
+store.subscribe(() => {
+    const { 1: route, 2: id } = location.hash.split('/')
+    if (route === 'categories') {
+        const catById = store.getState().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().goodById?.payload
+        if (goodById) { //вывести в main страницу товара
+            while (main.lastChild) {
+                main.lastChild.remove()
+            }
+            goodDraw(goodById.data.GoodFind[0], main)
+            let count = document.createElement("input")
+            count.type = "number"
+            count.min = 1
+            count.value = 1
+            let goodBtnBox = document.createElement("div")
+            let btn1 = document.createElement("button")
+            let btn2 = document.createElement("button")
+            goodBtnBox.style.display = "flex"
+            goodBtnBox.style.alignItems = "center"
+            btn1.textContent = "Купить"
+            btn2.textContent = "В корзину"
+            goodBtnBox.append(count, btn2, btn1)
+            main.lastChild.append(goodBtnBox)
+            let value = 1;
+
+            count.oninput = () => value = +count.value
+
+            let order = {
+                [id]: value
+            }
+
+            btn1.onclick = () => {
+                store.dispatch(actionBuyGood(order))
+            }
+            /* btn2.onclick = () => {
+                 store.dispatch(actionGoodOrder(order))
+             } */
+        }
+    }
+})
+
+let goodDraw = (obj, parent) => {
+    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")
+
+    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
+
+    box.append(goodIMG, goodName, description, price)
+    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)
+}