瀏覽代碼

HW js17 done

Ivar 2 年之前
父節點
當前提交
3492c64379
共有 4 個文件被更改,包括 269 次插入201 次删除
  1. 51 43
      js/17_redux-thunk-2/index.js
  2. 29 41
      js/18_redux-thunk-3/index.html
  3. 160 117
      js/18_redux-thunk-3/index.js
  4. 29 0
      js/18_redux-thunk-3/style.css

+ 51 - 43
js/17_redux-thunk-2/index.js

@@ -242,14 +242,15 @@ const actionFullRegister = (login, password) => (
 
 
 
-const actionRootCats = () => 
+const actionRootCats = () => (
     actionPromise('rootCats', gql(`query {
         CategoryFind(query: "[{\\"parent\\":null}]"){
             _id name
         }
     }`))
+)
 
-const actionCatById = (_id) =>  //добавить подкатегории
+const actionCatById = (_id) => ( //добавить подкатегории
     actionPromise('catById', gql(`query catById($q: String){
         CategoryFindOne(query: $q){
             _id name goods {
@@ -262,10 +263,10 @@ const actionCatById = (_id) =>  //добавить подкатегории
             }
         }
     }`, {q: JSON.stringify([{_id}])}))
+)
+// actionGoodById по аналогии
 
-// //actionGoodById по аналогии
-
-const actionGoodById = (_id) => 
+const actionGoodById = (_id) => (
     actionPromise('goodById', gql(`query goodById($q: String) {
         GoodFindOne(query: $q) {
             _id name price description images {
@@ -273,9 +274,9 @@ const actionGoodById = (_id) =>
             }
         }
     }`, {q: JSON.stringify([{_id}])}))
+)
 
-
-const actionGoodsByUser = () => 
+const actionGoodsByUser = () => (
 actionPromise('goodByUser', gql(`query oUser($query: String) {
     OrderFind(query:$query){
        _id orderGoods{
@@ -294,7 +295,7 @@ actionPromise('goodByUser', gql(`query oUser($query: String) {
     }
   }`, 
    {query: JSON.stringify([{}])}))
-
+)
 
   
 store.dispatch(actionRootCats())
@@ -304,23 +305,24 @@ store.subscribe(() => {
     const {rootCats} = promise
     if (rootCats?.payload) {
 
-
         aside.innerHTML = ''
+
         const regBtn = document.createElement('a')
         regBtn.href = `#/register`
         regBtn.innerText = 'Register'
+
         const loginBtn = document.createElement('a')
         loginBtn.href = `#/login`
         loginBtn.innerText = 'Login'
+
         const logoutBtn = document.createElement('button')
         logoutBtn.innerText = 'Logout'
         aside.append(regBtn, loginBtn, logoutBtn)
 
-
-
         logoutBtn.onclick = () => {
             store.dispatch(actionAuthLogout())
         }
+
         for (const {_id, name} of rootCats?.payload) {
             const link = document.createElement('a')
             link.href = `#/category/${_id}`
@@ -345,16 +347,12 @@ window.onhashchange = () => {
             console.log('страница товара')                
         },
         register(){
-            createForm(main, 'Register')
-            btnRegister.onclick = () => {
-                store.dispatch(actionFullRegister(loginRegister.value, passRegister.value))
-            }
+            let goRegister = createForm(main, 'Register', actionFullRegister)
+            goRegister()
         },
         login(){
-            createForm(main, 'Login')
-            btnLogin.onclick = () => {
-                store.dispatch(actionFullLogin(loginLogin.value, passLogin.value))
-            }
+            let goLogin = createForm(main, 'Login', actionFullLogin)
+            goLogin()
         },
         orders(){
             store.dispatch(actionGoodsByUser())
@@ -365,12 +363,17 @@ window.onhashchange = () => {
     }
 }
 
-function createForm(parent, type) {
+
+function createForm(parent, type, callback) {
     parent.innerHTML = ` 
     <input id="login${type}" type="text"/>
     <input id="pass${type}" type="password"/>
     <button id="btn${type}">${type}</button>
     `
+    return () => window[`btn${type}`].onclick = () => {
+                    store.dispatch(callback(window[`login${type}`].value, window[`pass${type}`].value))
+                    window[`pass${type}`].value = ''
+                }
 }
 
 window.onhashchange()
@@ -452,22 +455,38 @@ store.subscribe(() => {
 store.subscribe(() => {
     const {promise} = store.getState()
     const {goodByUser} = promise
-    const [,route, _id] = location.hash.split('/')
+    const [,route] = location.hash.split('/')
     if (goodByUser?.payload && route === 'orders'){
 
         main.innerHTML = ''   
-       
-        if (goodByUser.payload.orderGoods) {
-            for (const {price, count, total, good: {name, images}} of goodByUser.payload.orderGoods){
-                const card      = document.createElement('div')
-                card.innerHTML = `<h2>${name}</h2>
-                                  <img src="${backURL}/${images[0].url}" />
-                                  <strong>Куплено ${count} по ${price} грн. Итого:${total}</strong>
-                                  <br>
-                                  <a href="#/good/${_id}">Перейти на страницу товара</a>
-                                `
-                main.append(card)
+
+        if (goodByUser.payload) {
+            let totalMoney = 0
+
+            for (const order of goodByUser.payload) {
+
+                if (order.orderGoods) {
+                    for (const {price, count, total, good} of order.orderGoods) {
+                        if (price !== null && count !== null && total !== null && good !== null) {
+                            totalMoney += total                            
+                            const {_id, name, images} = good                            
+
+                            const card      = document.createElement('div')
+                            card.innerHTML  = `<h2>${name}</h2>
+                                                 <img src="${backURL}/${images[0].url}" />
+                                                 <strong>Куплено ${count} по ${price} грн. Итого:${total}</strong>
+                                                 <br>
+                                                 <a href="#/good/${_id}">Перейти на страницу товара</a>
+                                             `
+                            main.append(card)
+                        }
+                    }
+                }
+
             }
+            const totalBlock = document.createElement('b')
+            totalBlock.innerText = 'Итого потрачено: ' + totalMoney + ' грн'
+            main.append(totalBlock)
         }
     }
 })
@@ -476,14 +495,3 @@ store.subscribe(() => {
 // // store.dispatch(actionPromise('delay2000', delay(2000)))     
 // // store.dispatch(actionPromise('luke', fetch('https://swapi.dev/api/people/1/').then(res => res.json())))
 
-
-
-
-
-
-
-
-
-
-
-

+ 29 - 41
js/18_redux-thunk-3/index.html

@@ -4,55 +4,43 @@
    <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>
+   <title>Shop</title>
+   <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
+   <link rel="stylesheet" href="./style.css">
    <style>
-      #mainContainer {
-          display: flex;
-      }
-      #aside {
-          width: 30%;
-      }
-      #aside > a {
-          display: block;
-      }
-      #main > a {
-          display: block;
-      }
-      #loginContainer  {
-         display: flex;
-         flex-direction: column;
-      }
+
   </style>
 </head>
+
    <body>
-      <header>
-          <div id="cartIcon"></div>
-      </header>
-      <div id="topContaner"></div>
-      <div id='mainContainer'>
-          <aside id='aside'>
-              Категории
-          </aside>          
-          <main id='main'>
-              Контент
-          </main>
-      </div>
+      <div class="wrapper">
 
-        <!-- <div id="regContainer"> 
-            <input id="loginReg" type="text"/>
-            <input id="passReg" type="password"/>
-            <button id="regBtn">REGISTER</button>
-        </div>
+        <header>
+          <a id="cartIcon" href="#/cart"></a>
 
-        <div id="loginContainer">
-            <input id="loginInput" type="text"/>
-            <input id="passInput" type="password"/>
-            <button id="loginBtn">LOGIN</button>
-            <button id="logoutBtn">LOGOUT</button>
+          <div id="topContaner"></div>
+        </header>
+
+  
+        <div id='mainContainer'>
+  
+            <aside id='aside'>
+                Категории
+            </aside>    
+  
+            <main id='main'>
+                
+              
+                Контент
+            </main>
+  
         </div>
-        -->
 
-      <script src='index.js'></script>
+      </div>
+
+        <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.10.2/dist/umd/popper.min.js" integrity="sha384-7+zCNj/IqJ95wo16oMtfsKbZ9ccEh31eOz1HGyDuCQ6wgnyJNSYdrPa03rtR1zdB" crossorigin="anonymous"></script>
+        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js" integrity="sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13" crossorigin="anonymous"></script>
+        <script src='index.js'></script>
   </body>
 
 </html>

+ 160 - 117
js/18_redux-thunk-3/index.js

@@ -46,9 +46,10 @@ function combineReducers(reducers) {
 
     }
 }
+
 const combinedReducer = combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer})
 const store = createStore(combinedReducer)
-// console.log(store.getState()) // {promise: {}, auth: {}}
+
 store.subscribe(() => console.log(store.getState()))
 
 
@@ -94,39 +95,41 @@ function authReducer(state, {type, token}) {
 const actionAuthLogin = (token) => ({type: 'AUTH_LOGIN', token})
 const actionAuthLogout = () => ({type: 'AUTH_LOGOUT'})
 
-// const loginStore = createStore(authReducer)
-
 
-// const inputToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOnsiaWQiOiI2MWE0ZGIyOWM3NTBjMTJiYTZiYTQwMjIiLCJsb2dpbiI6ImVxd2VxZXdldyIsImFjbCI6WyI2MWE0ZGIyOWM3NTBjMTJiYTZiYTQwMjIiLCJ1c2VyIl19LCJpYXQiOjE2MzgxOTQ1NzZ9.Pi1GO6x7wdNrIrUKCQT-32-SsqmgFY-oFDrrXmw74-8'
-
-// loginStore.dispatch(actionAuthLogin(inputToken))
-// loginStore.dispatch(actionAuthLogout())
-// console.log(store.getState())
 
 function cartReducer (state={}, {type, good={}, count=1}) {
     if (!state) {
         return {}
     }
 
-    // только если в функции задан count по умолчанию
-    count = +count
-    if (!count) {
-        return state
-    }
+    // только если в функции задан count по умолчанию вызывать тут
+    // а так лучше вызвать в типах add и change
+        // count = +count
+        // if (!count) {
+        //     return state
+        // }
 
     const {_id} = good
 
     const types = {
         CART_ADD() {
+            count = +count
+            if (!count) {
+                return state
+            }
             return {
                 ...state,
-                [_id]: {good, count: count + (state[_id]?.count || 0)}
+                [_id]: {good, count: (count + (state[_id]?.count || 0)) < 0 ? 0 : count + (state[_id]?.count || 0)}
             }
         },
         CART_CHANGE() {
+            count = +count
+            if (!count) {
+                return state
+            }
             return {
                 ...state,
-                [_id]: {good, count}
+                [_id]: {good, count: count < 0 ? 0 : count}
             }
         },
         CART_REMOVE() {           
@@ -181,21 +184,6 @@ 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 promiceStore = createStore(promiseReducer)
-// store.subscribe(() => console.log(store.getState()))
-
-
-// const delay = (ms) => new Promise((ok) => setTimeout(() => ok(ms), ms))
-
-// // store.dispatch(actionPending('delay1000'))
-// // delay(1000).then(data => store.dispatch(actionResolved('delay1000', data)),
-// //                  error => store.dispatch(actionRejected('delay1000', error)))
-
-// // store.dispatch(actionPending('delay2000'))
-// // delay(2000).then(data => store.dispatch(actionResolved('delay2000', data)),
-// //                  error => store.dispatch(actionRejected('delay2000', error)))
-
-
 
 const actionPromise = (name, promise) => 
     async (dispatch) => {
@@ -212,7 +200,6 @@ const actionPromise = (name, promise) =>
 
 const getGQL = url =>
   async (query, variables={}) => {
-    // try {
       let obj = await fetch(url, {
         method: 'POST',
         headers: {
@@ -227,10 +214,6 @@ const getGQL = url =>
       } else {
           return a.data[Object.keys(a.data)[0]]
       }      
-    // }
-    // catch (error) {
-    //   console.log('Что-то не так, Бро ' + error);
-    // }
   }
 
   const backURL = 'http://shop-roles.asmer.fs.a-level.com.ua/'
@@ -299,30 +282,16 @@ const actionFullRegister = (login, password) => (
 )
 
 
-// regBtn.onclick = () => {
-//     store.dispatch(actionFullRegister(loginReg.value, passReg.value))
-// }
-
-
-// loginBtn.onclick = () => {
-//     store.dispatch(actionFullLogin(loginInput.value, passInput.value))
-// }
 
-// logoutBtn.onclick = () => {
-//     store.dispatch(actionAuthLogout())
-// }
-
-
-
-
-const actionRootCats = () => 
+const actionRootCats = () => (
     actionPromise('rootCats', gql(`query {
         CategoryFind(query: "[{\\"parent\\":null}]"){
             _id name
         }
     }`))
+)
 
-const actionCatById = (_id) =>  //добавить подкатегории
+const actionCatById = (_id) => (
     actionPromise('catById', gql(`query catById($q: String){
         CategoryFindOne(query: $q){
             _id name goods {
@@ -335,9 +304,9 @@ const actionCatById = (_id) =>  //добавить подкатегории
             }
         }
     }`, {q: JSON.stringify([{_id}])}))
+)
 
-
-const actionGoodById = (_id) => 
+const actionGoodById = (_id) => (
     actionPromise('goodById', gql(`query goodById($q: String) {
         GoodFindOne(query: $q) {
             _id name price description images {
@@ -345,28 +314,28 @@ const actionGoodById = (_id) =>
             }
         }
     }`, {q: JSON.stringify([{_id}])}))
+)
 
-
-const actionGoodsByUser = () => 
-actionPromise('goodByUser', gql(`query oUser($query: String) {
-    OrderFind(query:$query){
-       _id orderGoods{
-            price count total good{
-                _id name categories{
-                name
-                }
-                images {
-                    url
+const actionGoodsByUser = () => (
+    actionPromise('goodByUser', gql(`query oUser($query: String) {
+        OrderFind(query:$query){
+        _id orderGoods{
+                price count total good{
+                    _id name categories{
+                    name
+                    }
+                    images {
+                        url
+                    }
                 }
+            } 
+            owner {
+            _id login
             }
-        } 
-        owner {
-           _id login
-          }
-    }
-  }`, 
-   {query: JSON.stringify([{}])}))
-
+        }
+    }`, 
+    {query: JSON.stringify([{}])}))
+)
 
   
 store.dispatch(actionRootCats())
@@ -403,6 +372,81 @@ store.subscribe(() => {
 })
 
 
+
+function createForm(parent, type, callback) {
+    parent.innerHTML = ` 
+    <input id="login${type}" type="text"/>
+    <input id="pass${type}" type="password"/>
+    <button id="btn${type}">${type}</button>
+    `
+    return () => window[`btn${type}`].onclick = () => {
+                    store.dispatch(callback(window[`login${type}`].value, window[`pass${type}`].value))
+                    window[`pass${type}`].value = ''
+                }
+}
+
+
+
+const createCartPage = (parent) => {
+    parent.innerHTML = '';
+    const {cart} = store.getState();
+    console.log(cart);
+
+    const clearBtn = document.createElement('button');
+    clearBtn.innerText = "ОЧИСТИТЬ КОРЗИНУ";
+
+    if(Object.keys(cart).length !== 0) {
+        main.append(clearBtn);
+    }
+    clearBtn.onclick = () => {
+        store.dispatch(actionCartClear());
+    }
+    for(const item in cart) {
+        console.log(cart[item])
+        const {good} = cart[item];
+        const {count, good: {_id: id, name: name, price: price, images: [{url}]}} = cart[item];
+        console.log(count, id, name, price, url);
+        const card = document.createElement('div');
+        card.innerHTML = `
+            <div>
+                <p>${name}</p>
+                <img src="${backURL}/${url}">
+                <p>${count}</p>
+                <p>${price}</p>
+            </div>
+        `;
+
+        const changeCount = document.createElement('input');
+        changeCount.type = 'number';
+        changeCount.value = count;
+        card.append(changeCount);
+        changeCount.oninput = () => {
+            store.dispatch(actionCartChange(good, changeCount.value))
+        };
+
+        const deleteGood = document.createElement('button');
+        deleteGood.innerText = 'X';
+        deleteGood.style.display = 'block';
+        card.append(deleteGood);
+        deleteGood.onclick = () => {
+            store.dispatch(actionCartRemove(good))
+        }
+
+        parent.append(card);
+    }
+
+    const sendOrder = document.createElement('button');
+    sendOrder.innerText = "ОФОРМИТЬ ЗАКАЗ";
+    if(Object.keys(cart).length !== 0) {
+        main.append(sendOrder);
+    }
+    sendOrder.onclick = () => {
+        store.dispatch(actionOrder())
+    }
+}
+
+
+
 // location.hash - адресная строка после решетки
 window.onhashchange = () => {
     const [,route, _id] = location.hash.split('/')
@@ -412,28 +456,23 @@ window.onhashchange = () => {
             store.dispatch(actionCatById(_id))
             console.log('страница категорий')           
         },
-        good(){ //задиспатчить actionGoodById
+        good(){ 
             store.dispatch(actionGoodById(_id))
             console.log('страница товара')                
         },
         register(){
-            createForm(main, 'Register')
-            btnRegister.onclick = () => {
-                store.dispatch(actionFullRegister(loginRegister.value, passRegister.value))
-            }
+            let goRegister = createForm(main, 'Register', actionFullRegister)
+            goRegister()
         },
         login(){
-            createForm(main, 'Login')
-            btnLogin.onclick = () => {
-                store.dispatch(actionFullLogin(loginLogin.value, passLogin.value))
-            }
+            let goLogin = createForm(main, 'Login', actionFullLogin)
+            goLogin()
         },
         orders(){
             store.dispatch(actionGoodsByUser())
         },
-        cart(){ //задиспатчить actionGoodById
-            console.log('СДЕЛАТЬ СТРАНИЦУ С ПОЗИЦИЯМИ, полями ввода количества, картинками')
-            console.log('и кнопкой, которая store.dispatch(actionOrder())')
+        cart(){ 
+            createCartPage(main)
         },     
     }
     if (route in routes) {
@@ -441,13 +480,14 @@ window.onhashchange = () => {
     }
 }
 
-function createForm(parent, type) {
-    parent.innerHTML = ` 
-    <input id="login${type}" type="text"/>
-    <input id="pass${type}" type="password"/>
-    <button id="btn${type}">${type}</button>
-    `
-}
+
+store.subscribe(() => {
+    const [,route] = location.hash.split('/')
+    if (route === 'cart') {
+        createCartPage(main);
+    }
+})
+
 
 window.onhashchange()
 
@@ -506,9 +546,6 @@ store.subscribe(() => {
             main.append(card);
         }
     }
-    //ТУТ ДОЛЖНА БЫТЬ ПРОВЕРКА НА НАЛИЧИЕ goodById в редакс
-    //и проверка на то, что сейчас в адресной строке адрес ВИДА #/good/АЙДИ
-    //в таком случае очищаем main и рисуем информацию про товар с подробностями
 )
 
 
@@ -535,22 +572,38 @@ store.subscribe(() => {
 store.subscribe(() => {
     const {promise} = store.getState()
     const {goodByUser} = promise
-    const [,route, _id] = location.hash.split('/')
+    const [,route] = location.hash.split('/')
     if (goodByUser?.payload && route === 'orders'){
 
         main.innerHTML = ''   
-       
-        if (goodByUser.payload.orderGoods) {
-            for (const {price, count, total, good: {name, images}} of goodByUser.payload.orderGoods){
-                const card      = document.createElement('div')
-                card.innerHTML = `<h2>${name}</h2>
-                                  <img src="${backURL}/${images[0].url}" />
-                                  <strong>Куплено ${count} по ${price} грн. Итого:${total}</strong>
-                                  <br>
-                                  <a href="#/good/${_id}">Перейти на страницу товара</a>
-                                `
-                main.append(card)
+
+        if (goodByUser.payload) {
+            let totalMoney = 0
+
+            for (const order of goodByUser.payload) {
+
+                if (order.orderGoods) {
+                    for (const {price, count, total, good} of order.orderGoods) {
+                        if (price !== null && count !== null && total !== null && good !== null) {
+                            totalMoney += total                            
+                            const {_id, name, images} = good                            
+
+                            const card      = document.createElement('div')
+                            card.innerHTML  = `<h2>${name}</h2>
+                                                 <img src="${backURL}/${images[0].url}" />
+                                                 <strong>Куплено ${count} по ${price} грн. Итого:${total}</strong>
+                                                 <br>
+                                                 <a href="#/good/${_id}">Перейти на страницу товара</a>
+                                             `
+                            main.append(card)
+                        }
+                    }
+                }
+
             }
+            const totalBlock = document.createElement('b')
+            totalBlock.innerText = 'Итого потрачено: ' + totalMoney + ' грн'
+            main.append(totalBlock)
         }
     }
 })
@@ -566,16 +619,6 @@ store.subscribe(() => {
     }
     cartIcon.innerText  = counter
 })
-// store.dispatch(actionPromise('delay1000', delay(1000)))
-// // store.dispatch(actionPromise('delay2000', delay(2000)))     
-// // store.dispatch(actionPromise('luke', fetch('https://swapi.dev/api/people/1/').then(res => res.json())))
-
-
-
-
-
-
-
 
 
 

+ 29 - 0
js/18_redux-thunk-3/style.css

@@ -0,0 +1,29 @@
+
+body {
+
+}
+
+.wrapper {
+   
+}
+
+#mainContainer {
+   display: flex;
+}
+
+#aside {
+   width: 30%;
+}
+
+#aside > a {
+   display: block;
+}
+
+#main > a {
+   display: block;
+}
+
+#loginContainer  {
+  display: flex;
+  flex-direction: column;
+}