Selaa lähdekoodia

added HW16,17 done

makstravm 3 vuotta sitten
vanhempi
commit
9bc96ce871
7 muutettua tiedostoa jossa 636 lisäystä ja 18 poistoa
  1. 11 18
      HW15/main.js
  2. 40 0
      HW16/index.html
  3. 177 0
      HW16/main.js
  4. 90 0
      HW16/style.css
  5. 38 0
      HW17/index.html
  6. 190 0
      HW17/main.js
  7. 90 0
      HW17/style.css

+ 11 - 18
HW15/main.js

@@ -1,23 +1,16 @@
 const getGQL = url =>
   async (query, variables) => {
-    try {
-      let obj = await fetch(url, {
-        method: 'POST',
-        headers: {
-          "Content-Type": "application/json"
-        },
-        body: JSON.stringify({ query, variables })
-      })
-      let a = await obj.json()
-      for (const key in a) {
-        for (const deepKey in a[key]) {
-          return a[key][deepKey]
-        }
-      }
-    }
-    catch (error) {
-      console.log('Что-то не так, Бро ' + error);
-    }
+
+    let obj = await fetch(url, {
+      method: 'POST',
+      headers: {
+        "Content-Type": "application/json"
+      },
+      body: JSON.stringify({ query, variables })
+    })
+    let obj = await obj.json()
+    if (!obj.data && obj.errors) throw new Error(JSON.stringify(obj.errors))
+    return obj.data[Object.keys(obj.data)[0]]
   }
 
 const gql = getGQL('http://shop-roles.asmer.fs.a-level.com.ua/graphql');

+ 40 - 0
HW16/index.html

@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html lang="en-ru">
+
+<html>
+<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>HW-17</title>
+<link rel="stylesheet" href="style.css">
+
+<body id="body">
+    <div class="container">
+        <div class="user">
+            <button class="login" id="userLogin">login</button>
+            <button class="register" id="userRegister">register</button>
+        </div>
+    </div>
+    <div class="container">
+        <div id='title'></div>
+        <div class="products">
+            <div class="menuWrapper">
+                <button id="menuBtn"> Burger Menu</button>
+                <ul id="menu"></ul>
+            </div>
+            <div id="itemBox" class="itemBox">
+
+            </div>
+        </div>
+
+
+    </div>
+
+
+    <!-- <div id=""></div> -->
+
+    <div id="overlay" class="overlay"></div>
+    <script src="main.js"></script>
+</body>
+
+</html>

+ 177 - 0
HW16/main.js

@@ -0,0 +1,177 @@
+
+const getGQL = url =>
+    async (query, variables) => {
+        try {
+            let obj = await fetch(url, {
+                method: 'POST',
+                headers: {
+                    Authorization: localStorage.authToken ? 'Bearer ' + localStorage.authToken : null,
+                    "Content-Type": "application/json"
+                },
+                body: JSON.stringify({ query, variables })
+            })
+            let a = await obj.json()
+            for (const key in a) {
+                for (const deepKey in a[key]) {
+                    return a[key][deepKey]
+                }
+            }
+        }
+        catch (error) {
+            console.log('Что-то не так, Бро ' + error);
+        }
+    }
+
+const gql = getGQL('http://shop-roles.asmer.fs.a-level.com.ua/graphql');
+
+function logIn() {
+    const loginBox = document.createElement('div')
+    loginBox.setAttribute('id', 'loginBox')
+    const inputNick = document.createElement('input')
+    const h2 = document.createElement('h2')
+    h2.innerText = 'Log In'
+    const inputPassword = document.createElement('input')
+    const btnLogIn = document.createElement('button')
+    const btnRegister = document.createElement('button')
+    const btnX = document.createElement('button')
+    btnX.innerText = 'X'
+    btnX.classList.add('close')
+    btnLogIn.innerText = 'Log In'
+    btnRegister.innerText = 'Register'
+    overlay.style.display = 'block'
+    btnLogIn.onclick = async () => {
+        let jwt = await gql(`
+    query NameForMe1($login:String, $password:String){
+        login(login:$login, password:$password)
+    }
+`, {
+            login: inputNick.value, password: inputPassword.value
+        })
+        console.log(jwt);
+        if (jwt) {
+            localStorage.setItem('authToken', jwt)
+            overlay.style.display = 'none'
+            loginBox.remove()
+            const h1 = document.createElement('h2')
+            h1.innerText = `Welcome ${inputNick.value}`
+            title.append(h1)
+        }
+    }
+    btnRegister.onclick = () => {
+        loginBox.remove()
+        registerF()
+    }
+    btnX.onclick = () => {
+        loginBox.remove()
+        overlay.style.display = 'none'
+    }
+    inputNick.type = 'text'
+    inputPassword.type = 'password'
+    loginBox.append(h2)
+    loginBox.append(inputNick)
+    loginBox.append(btnX)
+    loginBox.append(inputPassword)
+    loginBox.append(btnLogIn)
+    loginBox.append(btnRegister)
+    body.append(loginBox)
+}
+
+function registerF() {
+    const loginBox = document.createElement('div')
+    loginBox.setAttribute('id', 'loginBox')
+    const inputNick = document.createElement('input')
+    const h2 = document.createElement('h2')
+    h2.innerText = 'Register'
+    const inputPassword = document.createElement('input')
+    const btnLogIn = document.createElement('button')
+    const btnRegister = document.createElement('button')
+    const btnX = document.createElement('button')
+    btnX.innerText = 'X'
+    btnX.classList.add('close')
+    btnLogIn.innerText = 'Enter login'
+    btnRegister.innerText = 'Register'
+    overlay.style.display = 'block'
+    btnRegister.onclick = async () => {
+        let jwt = await gql(`
+            mutation reg($login:String, $password:String){
+            UserUpsert(user:{
+                login:$login,
+      			password:$password,
+      			nick:$login}){
+            _id login
+            }
+        }
+        `, {
+            login: inputNick.value, password: JinputPassword.value
+        })
+        console.log(jwt);
+    }
+    btnLogIn.onclick = () => {
+        loginBox.remove()
+        logIn()
+    }
+    btnX.onclick = () => {
+        loginBox.remove()
+        overlay.style.display = 'none'
+    }
+    inputNick.type = 'text'
+    inputPassword.type = 'password'
+    loginBox.append(h2)
+    loginBox.append(inputNick)
+    loginBox.append(inputPassword)
+    loginBox.append(btnRegister)
+    loginBox.append(btnLogIn)
+    loginBox.append(btnX)
+    body.append(loginBox)
+}
+userLogin.onclick = () => {
+    overlay.style.display = 'block'
+    logIn()
+}
+userRegister.onclick = () => {
+    overlay.style.display = 'block'
+    registerF()
+}
+overlay.onclick = () => {
+    overlay.style.display = 'none'
+    loginBox.remove()
+}
+
+menuBtn.onclick = async () => {
+    let cat = await gql(`
+        query Categoria {
+        CategoryFind(query:"[{}]"){
+        name _id goods{
+            name images {
+                    url
+                }
+            }
+        }
+    }
+`, { login: 'Kabina', password: '12345' })
+    cat.map(c => {
+        if (c) {
+            let li = document.createElement('li')
+            let btnLink = document.createElement('button')
+            btnLink.classList.add('menuBtn')
+            btnLink.setAttribute('id', c._id)
+            btnLink.innerText = `${c.name}  к-во:${c.goods ? c.goods.length : '0'}`
+            btnLink.onclick = () => {
+                itemBox.firstChild ? itemBox.firstChild.remove() : null
+                let items = document.createElement('items')
+                if (c.goods !== null) {
+                    c.goods.map(i => {
+                        let item = document.createElement('item')
+                        let h3 = document.createElement('h3')
+                        h3.innerText = i.name
+                        item.append(h3)
+                        items.append(item)
+                    })
+                    itemBox.append(items)
+                }
+            }
+            li.append(btnLink)
+            menu.append(li)
+        }
+    })
+}

+ 90 - 0
HW16/style.css

@@ -0,0 +1,90 @@
+:root {
+  box-sizing: border-box;
+  font-size: 16px;
+  line-height: 20px;
+  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
+}
+.container {
+  max-width: 1200px;
+  padding: 0 15px;
+  margin: 0 auto;
+}
+
+#loginBox {
+  width: 50%;
+  height: 50%;
+  padding: 20px 15px;
+  border-radius: 15px;
+  z-index: 5;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  background-color: #fff;
+  display: flex;
+  flex-wrap: wrap;
+  align-content: flex-start;
+  justify-content: center;
+}
+#loginBox input {
+  width: 100%;
+  margin-bottom: 10px;
+  padding: 10px 5px;
+  font-size: 1.5em;
+  line-height: 1em;
+}
+
+button {
+  cursor: pointer;
+  padding: 10px 5px;
+  margin: 0 10px 10px;
+}
+#overlay {
+  background-color: rgba(0, 0, 0, 0.3);
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 0;
+  bottom: 0;
+  display: none;
+}
+#loginBox .close {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  background-color: #ececec;
+  border: none;
+  border-radius: 50%;
+  padding: 0;
+  margin: 0;
+  width: 25px;
+  height: 25px;
+}
+ul {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.menuBtn {
+  background-color: transparent;
+  border: none;
+  font-weight: 700;
+  margin: 0;
+  padding: 0;
+  border-bottom: 1px solid #000;
+  margin-bottom: 10px;
+  transition: all 0.3s;
+}
+.menuBtn:hover {
+  color: red;
+}
+.products {
+  display: flex;
+  justify-content: space-between;
+}
+.itemBox {
+  display: flex;
+  justify-content: space-between;
+  width: 75%;
+}

+ 38 - 0
HW17/index.html

@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en-ru">
+
+<html>
+<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>less-19</title>
+<link rel="stylesheet" href="style.css">
+<style>
+    #mainContainer {
+        display: flex;
+    }
+
+    #aside {
+        width: 30%;
+    }
+
+    #aside>a {
+        display: block;
+    }
+</style>
+</head>
+
+<body>
+    <header>КУДА Я ПОПАЛ?</header>
+    <div id='mainContainer'>
+        <aside id='aside'>
+            Категории
+        </aside>
+        <main id='main'>
+            Контент
+        </main>
+    </div>
+    <script src="main.js"></script>
+</body>
+
+</html>

+ 190 - 0
HW17/main.js

@@ -0,0 +1,190 @@
+function createStore(reducer) {
+    let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
+    let cbs = []                     //массив подписчиков
+
+    const getState = () => state            //функция, возвращающая переменную из замыкания
+    const subscribe = cb => (cbs.push(cb),   //запоминаем подписчиков в массиве
+        () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
+
+    const dispatch = action => {
+        if (typeof action === 'function') { //если action - не объект, а функция
+            return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы
+        }
+        const newState = reducer(state, action) //пробуем запустить редьюсер
+        if (newState !== state) { //проверяем, смог ли редьюсер обработать action
+            state = newState //если смог, то обновляем state 
+            for (let cb of cbs) cb() //и запускаем подписчиков
+        }
+    }
+
+    return {
+        getState, //добавление функции getState в результирующий объект
+        dispatch,
+        subscribe //добавление subscribe в объект
+    }
+}
+
+function promiseReducer(state = {}, { type, status, payload, error, name }) {
+    if (type === 'PROMISE') {
+        return {
+            ...state,
+            [name]: { status, payload, error }
+        }
+    }
+    return state;
+}
+
+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 data = await promise
+            dispatch(actionResolved(name, data))
+            return data
+        }
+        catch (error) {
+            dispatch(actionRejected(name, error))
+        }
+    }
+
+const store = createStore(promiseReducer)
+store.subscribe(() => console.log(store.getState()))
+
+//const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
+
+const getGQL = url =>
+    async (query, variables = {}) => {
+        let obj = await fetch(url, {
+            method: 'POST',
+            headers: {
+                "Content-Type": "application/json"
+            },
+            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]]
+    }
+
+const backURL = 'http://shop-roles.asmer.fs.a-level.com.ua'
+const gql = getGQL(backURL + '/graphql');
+
+const actionRootCats = () =>
+    actionPromise('rootCats', gql(`query {
+        CategoryFind(query: "[{\\"parent\\":null}]"){
+            _id name
+        }
+    }`))
+
+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 }]) }))
+
+//actionGoodById по аналогии
+
+const actionGoodById = (_id) =>  //добавить подкатегории
+    actionPromise('goodById', gql(`query goodByID($q: String) {
+                                    GoodFind(query: $q){
+ 	 	                                name _id  description price images{url}
+  }
+}`, { q: JSON.stringify([{ _id }]) }))
+
+store.dispatch(actionRootCats())
+store.dispatch(actionGoodById())
+
+store.subscribe(() => {
+    const { rootCats } = store.getState()
+    if (rootCats?.payload) {
+        aside.innerHTML = ''
+        for (const { _id, name } of rootCats?.payload) {
+            const link = document.createElement('a')
+            link.href = `#/category/${_id}`
+            link.innerText = name
+            aside.append(link)
+        }
+    }
+})
+
+window.onhashchange = () => {
+    const [, route, _id] = location.hash.split('/')
+
+    const routes = {
+        category() {
+            store.dispatch(actionCatById(_id))
+        },
+        good() { //задиспатчить actionGoodById
+            store.dispatch(actionGoodById(_id))
+        },
+    }
+    if (route in routes)
+        routes[route]()
+}
+
+window.onhashchange()
+
+store.subscribe(() => {
+    const { catById } = store.getState()
+    const [, route, _id] = location.hash.split('/')
+    if (catById?.payload && route === 'category') {
+        const { name, subCategories } = catById.payload
+        main.innerHTML = `<h1>${name}</h1> `
+        subCategories ? subCategories.map(s => {
+            const link = document.createElement('a')
+            link.href = `#/category/${s._id}`
+            link.innerText = s.name
+            main.append(link)
+        }) : ''
+        for (const { _id, name, price, images } of catById.payload.goods) {
+            const card = document.createElement('div')
+            card.innerHTML = `<h2>${name}</h2>
+                            <img src="${backURL}/${images[0].url}" />
+                            <strong>${price}</strong>`
+            const link = document.createElement('a')
+            link.href = `#/good/${_id}`
+            link.innerText = name
+            card.append(link)
+            main.append(card)
+        }
+    }
+})
+
+store.subscribe(() => {
+    const { goodById } = store.getState()
+    const [, route, _id] = location.hash.split('/')
+    if (goodById?.payload && route === 'good') {
+        main.innerHTML = ''
+        const { name, description, price, images } = goodById.payload[0]
+        main.innerHTML = `
+        <div div class="product" >
+            <div class="product__img">
+                <img src="${backURL}/${images[0].url}" />
+            </div>
+            <div class="product__inner">
+                <h2 class="product_title">${name}</h2>
+                <p class="product__price"> <strong>${price}</strong></p>
+                <p class="product__description">
+                    <span>Обзор: ${description}</span>
+                </p>
+            </div>
+        </div>`
+    }
+})
+
+
+
+//store.dispatch(actionPromise('', delay(1000)))
+//store.dispatch(actionPromise('delay2000', delay(2000)))
+//store.dispatch(actionPromise('luke', fetch('https://swapi.dev/api/people/1/').then(res => res.json())))
+

+ 90 - 0
HW17/style.css

@@ -0,0 +1,90 @@
+:root {
+  box-sizing: border-box;
+  font-size: 16px;
+  line-height: 20px;
+  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
+}
+.container {
+  max-width: 1200px;
+  padding: 0 15px;
+  margin: 0 auto;
+}
+
+#loginBox {
+  width: 50%;
+  height: 50%;
+  padding: 20px 15px;
+  border-radius: 15px;
+  z-index: 5;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  background-color: #fff;
+  display: flex;
+  flex-wrap: wrap;
+  align-content: flex-start;
+  justify-content: center;
+}
+#loginBox input {
+  width: 100%;
+  margin-bottom: 10px;
+  padding: 10px 5px;
+  font-size: 1.5em;
+  line-height: 1em;
+}
+
+button {
+  cursor: pointer;
+  padding: 10px 5px;
+  margin: 0 10px 10px;
+}
+#overlay {
+  background-color: rgba(0, 0, 0, 0.3);
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 0;
+  bottom: 0;
+  display: none;
+}
+#loginBox .close {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  background-color: #ececec;
+  border: none;
+  border-radius: 50%;
+  padding: 0;
+  margin: 0;
+  width: 25px;
+  height: 25px;
+}
+ul {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.menuBtn {
+  background-color: transparent;
+  border: none;
+  font-weight: 700;
+  margin: 0;
+  padding: 0;
+  border-bottom: 1px solid #000;
+  margin-bottom: 10px;
+  transition: all 0.3s;
+}
+.menuBtn:hover {
+  color: red;
+}
+.products {
+  display: flex;
+  justify-content: space-between;
+}
+.itemBox {
+  display: flex;
+  justify-content: space-between;
+  width: 75%;
+}