Jelajahi Sumber

HW YB2NKR8B2LL done

Varvara Huza 3 tahun lalu
induk
melakukan
9ee02329a0
4 mengubah file dengan 565 tambahan dan 71 penghapusan
  1. 1 70
      Homework_15/main.js
  2. 0 1
      Homework_17/main.js
  3. 94 0
      Homework_18/index.html
  4. 470 0
      Homework_18/main.js

+ 1 - 70
Homework_15/main.js

@@ -78,73 +78,4 @@ async function speedtest(getPromise, count, parallel=1){
     }
 
 speedtest(() => delay(1000), 10, 10 ).then(result => console.log(result))
-speedtest(() => fetch('http://swapi.dev/api/people/1').then(res => res.json()), 3, 1).then(result => console.log(result))
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-// async function newTrafficLight(element, timeGreen, timeYellow, timeRed) {
-//     async function trafficLightImproved(){
-//         while (a){
-//             element.style.backgroundColor = 'green'
-//             if (!a) break
-//             await delay(timeGreen)
-//             element.style.backgroundColor = 'yellow'
-//             if (!a) break
-//             await delay(timeYellow)
-//             element.style.backgroundColor = 'red'
-//             if (!a) break
-//             await delay(timeRed)
-//         }
-//     }
-
-//     let a = true;
-
-//     // btn.onclick = async () => {
-//     //     a = false;
-//     //     await delay(3000)
-//     //     a = true;
-//     //     trafficLightImproved()
-//     // }
-
-//     trafficLightImproved()
-//     while (true) {
-//         await domEventPromise(btn, 'click').then(e => console.log('event click happens', e))
-//         .then(async () => {
-//             a = false
-//             await delay(1000)
-//             a = true
-//             trafficLightImproved()
-//         })
-//         await delay(5000)
-//     }
-// }
-
-// newTrafficLight(document.body, 5000, 2000, 5000)
-
-
+speedtest(() => fetch('http://swapi.dev/api/people/1').then(res => res.json()), 3, 1).then(result => console.log(result))

+ 0 - 1
Homework_17/main.js

@@ -33,7 +33,6 @@ function promiseReducer(state={}, {type, status, payload, error, name}){
     return state
 }
 
-
 const store = createStore(promiseReducer)
 const unsubscribe1 = store.subscribe(() => console.log(store.getState()))
 

+ 94 - 0
Homework_18/index.html

@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+    <head>
+        <title>GQL</title>
+        <meta charset='utf8' />
+        <style>
+            body {
+                margin: 0;
+                padding: 0;
+            }
+
+            a {
+                color: #23217c;
+                text-decoration: none;
+            }
+
+            a:hover {
+                color: #1916dd;
+            }
+
+            input {
+                margin-bottom: 10px;
+            }
+
+            header {
+                background-color: #23217c;
+                color: #fff;
+                font-weight: bold;
+                padding: 10px 25px;
+            }
+
+            header span {
+                margin-right: 25px;
+            }
+
+            header a {
+                color: #fff;
+                cursor: pointer;
+            }
+
+            header a:hover {
+                color: #fff;
+                text-decoration: underline;
+            }
+
+            #mainContainer {
+                display: flex;
+                padding: 15px 0;
+            }
+
+            #aside {
+                width: 30%;
+                padding: 0 25px;
+            }
+
+            #aside > a{
+                display: block;
+            }
+
+            #main > a {
+                display: block;
+                margin: 15px 0 5px 0;
+            }
+
+            #inputs {
+                display: flex;
+                flex-direction: column;
+            }
+
+            .good-container {
+                padding: 5px;
+                margin-bottom: 15px;
+                display: flex;
+                flex-direction: column;
+            }
+        </style>
+    </head>
+    <body>
+        <header>
+            <span>КУДА Я ПОПАЛ?</span>
+            <span id='login'></span>
+            <span><a href="#/cart"> =&gt; корзина тута &lt;= </a></span>
+        </header>
+        <div id='mainContainer'>
+            <aside id='aside'>
+                Категории
+            </aside>
+            <main id='main'>
+                Выберите категорию пожалуйста будьте так любезны 
+            </main>
+        </div>
+        <script src='main.js'></script>
+    </body>
+</html>

+ 470 - 0
Homework_18/main.js

@@ -0,0 +1,470 @@
+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 combineReducers(reducers={cart: cartReducer, promise: promiseReducer, auth: authReducer}){
+    return (state={}, action) => {
+        let newState = {}
+        for (let key in reducers) {
+            let newSubState = reducers[key](state[key], action)
+            if(newSubState !== state[key]) {
+                newState[key] = newSubState
+            }
+        }
+        if (Object.keys(newState).length) {
+            return {...state, ...newState}
+        } else {
+            return state
+        }
+    }
+}
+
+//promise 
+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 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 function(query, variables={}) {
+        return fetch(url, 
+        {
+            method: "POST",
+            headers: 
+            {"Content-Type": "application/json",
+            ...(localStorage.authToken ? {Authorization: 'Bearer ' + localStorage.authToken} : {})
+            },
+            body: JSON.stringify({query, variables})
+        }).then(resp => resp.json())
+        .then(data => {
+            if ("errors" in data) {
+                let error = new Error('ашипка, угадывай што не так')
+                throw error
+            }
+            else {
+                return data.data[Object.keys(variables)[0]]
+            }
+        })
+    }
+}
+
+let shopGQL = getGQL('http://shop-roles.asmer.fs.a-level.com.ua/graphql')
+
+const goodById = goodId => {
+    let id = `[{"_id":"${goodId}"}]`
+    return shopGQL(`
+    query good($id:String){
+        GoodFindOne(query: $id) {
+          _id name description price images {
+            _id text url
+          }
+          categories {
+            _id name
+          }
+        }
+    }`, {GoodFindOne: '', id })
+}
+
+const actionGoodById = id => 
+    actionPromise('goodById', goodById(id))
+
+const actionRootCategories = () =>
+    actionPromise('rootCategories', shopGQL(`
+            query cats($query:String){
+              CategoryFind(query:$query){
+                _id name 
+              }
+            }
+        `, {CategoryFind:'', query: JSON.stringify([{parent:null}])}))
+
+const actionCategoryById = (_id) =>
+    actionPromise('catById', shopGQL(`
+    query catById($query:String){
+        CategoryFindOne(query:$query){
+        _id name goods{
+            _id name price description images{
+            url
+            }
+        }
+        }
+    }`, { CategoryFindOne: '', query: JSON.stringify([{ _id }]) }))
+
+const actionAddOrder = (cart) => {
+    let string = '['
+    for (let goodId in cart) {
+        string += `{good: {_id: "${goodId}"}, count: ${cart[goodId].count}},`
+
+    }
+    string = string.slice(0, string.length - 1)
+    string += ']'
+    store.dispatch(actionPromise('orderAdd', shopGQL(`
+    mutation {
+        OrderUpsert(order: {
+          orderGoods: ${string}
+        }) {
+          _id total
+        }
+      }`, {OrderUpsert: ''})))
+}
+
+//cart
+function cartReducer(state={}, {type, good, price, count=1}) {
+    if (Object.keys(state).length === 0 && localStorage.cart.length > 10) {
+        let newState = JSON.parse(localStorage.cart)
+        return newState
+    }
+    const types = {
+        CART_ADD() {
+            let newState = {
+             ...state,
+             [good._id]: {count: (state[good._id]?.count || 0) + count, good: {id: good._id, name: good.name}, price}
+            }
+            localStorage.cart = JSON.stringify(newState)
+            return newState
+        },
+        CART_REMOVE() {
+            let {[good._id]:poh, ...newState} = state
+            localStorage.cart = JSON.stringify(newState)
+            return newState
+            // let newState = {...state}
+            // delete newState[good._id]
+            // return newState
+        },
+        CART_CLEAR() {
+            let newState = {}
+            localStorage.cart = JSON.stringify(newState)
+            return newState
+        },
+        CART_SET() {
+            let newState = {
+                ...state,
+                [good._id]: {count, good: {id: good._id, name: good.name}, price}
+            }
+            localStorage.cart = JSON.stringify(newState)
+            return newState
+        }
+    }
+    if (type in types) {
+        return types[type]()
+    }
+    return state
+}
+
+const actionCartAdd = (_id, name, price, count) => ({type: 'CART_ADD', good: {_id, name}, price, count})
+const actionCartRemove = (_id, name) => ({type: 'CART_REMOVE', good: {_id, name}})
+const actionCartSet = (_id, name, price, count) => ({type: 'CART_SET', good: {_id, name}, price, count})
+const actionCartClear = () => ({type: 'CART_CLEAR'})
+
+//auth
+const jwt_decode = (jwt) => {
+    let payload = jwt.split('.')
+    return JSON.parse(atob(payload[1]))
+}
+
+function authReducer(state, action={}){ //....
+    if (state === undefined){
+        //добавить в action token из localStorage, и проимитировать LOGIN (action.type = 'LOGIN')
+        if (localStorage.authToken) {
+            action.jwt = localStorage.authToken
+            return {token: action.jwt, payload: jwt_decode(action.jwt)}
+        }
+    }
+    if (action.type === 'LOGIN'){
+        console.log('ЛОГИН')
+        //+localStorage
+        //jwt_decode
+        return {token: action.jwt, payload: jwt_decode(action.jwt)}
+    }
+    if (action.type === 'LOGOUT'){
+        console.log('ЛОГАУТ')
+        //-localStorage
+        //вернуть пустой объект
+        return {}
+    }
+    if (action.type === 'LOGGING_IN'){
+        return {loginPageHello: true}
+    }
+    return state
+}
+
+const actionLogin = (jwt) => ({type: 'LOGIN', jwt})
+const thunkLogin = (login, password) => {
+    return (dispatch) => {
+        shopGQL(`query login($login:String, $password: String) {login(login:$login, password:$password)}`, { login, password })
+        .then(jwt => {
+            if (jwt) {
+                localStorage.authToken = jwt
+                dispatch(actionLogin(jwt))
+            } else {
+                throw new Error('wrong')
+            }
+        })
+    }
+}
+
+const actionLogout = () => ({type: 'LOGOUT'})
+const thunkLogout = () => {
+    return (dispatch) => {
+            localStorage.authToken = ''
+            localStorage.cart = ''
+            store.dispatch(actionCartClear())
+            dispatch(actionLogout())
+    }
+}
+
+const actionLoggingIn = () => ({type: 'LOGGING_IN'})
+
+//store
+const store = createStore(combineReducers({cart: cartReducer, promise: promiseReducer, auth: authReducer}))
+const unsubscribe1 = store.subscribe(() => console.log(store.getState()))
+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 === 'login'){
+        store.dispatch(actionLoggingIn())
+    }
+    if (route === 'cart'){
+        drawCart()
+    }
+}
+
+function drawMainMenu(){
+    let cats = store.getState().promise.rootCategories.payload
+    if (cats){ //каждый раз дорисовываются в body
+        aside.innerText = ''
+        for (let {_id, name} of cats){
+            let catA = document.createElement('a')
+            catA.href = `#/categories/${_id}`
+            catA.innerText = name
+            aside.append(catA)
+        }
+    }
+}
+
+function drawHeader() {
+    login.innerHTML = (store.getState().auth?.payload ? `${store.getState().auth.payload.sub.login} | <a id='logout' href="#/login">Log out</a>` : '<a href="#/login">Log in</a>')
+    if (document.querySelector('#logout')) {
+        logout.onclick = () => {
+            store.dispatch(thunkLogout())
+        }
+    }
+}
+
+function drawCart() {
+    let cart = store.getState().cart
+        if (!localStorage.authToken) {
+            main.innerText = 'Залогинтесь плез'
+        } else if (!Object.keys(cart).length) {
+            main.innerText = 'Корзина пуста'
+        } else {
+            main.innerText = 'Ваша корзина: '
+            for (let goodId in cart) {
+                let {good: {id, name}, price, count} = cart[goodId]
+                let goodContainer = document.createElement('div')
+                goodContainer.classList.add('good-container')
+                let goodName = document.createElement('div')
+                goodName.innerText = name
+                let goodPrice = document.createElement('div')
+                goodPrice.innerText = 'Стоимость: ' + price
+                let goodCount = document.createElement('input')
+                goodCount.type = 'number'
+                goodCount.value = count
+                goodCount.onchange = () => {
+                    store.dispatch(actionCartSet(id, name, price, (goodCount.value > 0 ? +goodCount.value : 1)))
+                }
+
+                let removeBtn = document.createElement('button')
+                removeBtn.innerText = 'Удалить товар'
+                removeBtn.onclick = () => {
+                    store.dispatch(actionCartRemove (id, name))
+                }
+
+                goodContainer.append(goodName, goodPrice, goodCount, removeBtn)
+                main.append(goodContainer)
+            }
+
+            let price = 0
+            for (let goodId in cart) {
+                price += cart[goodId].price * cart[goodId].count
+            }
+            let totalPriceContainer = document.createElement('div')
+            totalPriceContainer.innerText = 'Общая стоимость: ' + price
+            main.append(totalPriceContainer)
+
+            let setOrderBtn = document.createElement('button')
+            setOrderBtn.innerText = 'Оформить заказ'
+            setOrderBtn.onclick = () => {
+                actionAddOrder(store.getState().cart)
+            }
+            main.append(setOrderBtn)
+
+            let clearBtn = document.createElement('button')
+            clearBtn.innerText = 'Очистить корзину'
+            clearBtn.onclick = () => {
+                store.dispatch(actionCartClear())
+            }
+            main.append(clearBtn)
+        }
+}
+
+function drawOrderSuccessful() {
+    if (store.getState().promise.orderAdd?.status === 'RESOLVED') {
+        let order = store.getState().promise.orderAdd.payload
+        main.innerText = 'Заказ оформился, всё круто'
+
+        let orderInfo = document.createElement('div')
+        orderInfo.innerText = `Номер заказа: ${order._id}. Стоимость: ${order.total}`
+        main.append(orderInfo)
+    }
+}
+
+store.subscribe(drawMainMenu)
+store.subscribe(drawHeader)
+store.subscribe(() => {
+    const {1: route, 2:id} = location.hash.split('/')
+    if (route === 'categories'){
+        const catById = store.getState().promise.catById?.payload
+        if (catById){
+            main.innerText = ''
+            let categoryName = document.createElement('div')
+            categoryName.innerText = catById.name
+            categoryName.style.fontSize = '25px'
+            categoryName.style.fontWeight = 'bold'
+            main.append(categoryName)
+            for (let {_id, name, price} of catById.goods){
+                let good = document.createElement('a')
+                    good.href = `#/good/${_id}`
+                    good.innerText = name
+
+                let btn = document.createElement('button')
+                    btn.onclick = () => {
+                        if (!localStorage.authToken) {
+                            main.innerText = 'Залогинтесь плез'
+                        } else {
+                            store.dispatch(actionCartAdd(_id, name, price))
+                        }
+                    }
+                    btn.style.cursor = 'pointer'
+                    btn.innerText = 'купыть'
+                main.append(good, btn)
+            }
+        }
+    }
+    if (route === 'good'){
+        const goodById = store.getState().promise.goodById?.payload
+        if (goodById){
+            main.innerText = ''
+            let {name, description, price, _id} = goodById
+            let goodName = document.createElement('div')
+                goodName.innerText = name
+                goodName.style.fontSize = '35px'
+                goodName.style.fontWeight = 'bold'
+                goodName.style.marginBottom = '25px'
+
+            let goodDescription = document.createElement('div')
+                goodDescription.innerText = description
+                goodDescription.style.marginBottom = '25px'
+
+            let goodPrice = document.createElement('div')
+                goodPrice.innerText = 'Цена: ' + price
+                goodPrice.style.marginBottom = '5px'
+
+            let btn = document.createElement('button')
+                btn.onclick = () => {
+                    if (!localStorage.authToken) {
+                        main.innerText = 'Залогинтесь плез'
+                    }  else {
+                        store.dispatch(actionCartAdd(_id, name, price))
+                    }
+                }
+                btn.style.cursor = 'pointer'
+                btn.innerText = 'купыть'
+
+            main.append(goodName, goodDescription, goodPrice, btn)
+        }
+    }
+    if (route === 'login') {
+        main.innerText = ''
+
+        let inputsContainer = document.createElement('div')
+        inputsContainer.id = 'inputs'
+        let loginInput = document.createElement('input')
+        loginInput.type = 'text'
+        let loginLabel = document.createElement('span')
+        loginLabel.innerText = 'Login:'
+        let passwordInput = document.createElement('input')
+        passwordInput.type = 'password'
+        let passwordLabel = document.createElement('span')
+        passwordLabel.innerText = 'Password:'
+        let button = document.createElement('button')
+        button.innerText = 'log in cyka'
+        button.onclick = () => {
+            if (loginInput.value && passwordInput.value){
+                store.dispatch(thunkLogin(loginInput.value, passwordInput.value))
+            }
+        }
+
+        inputsContainer.append(loginLabel, loginInput, passwordLabel, passwordInput, button)
+        main.append(inputsContainer)
+
+        if (store.getState().auth?.payload) {
+            button.disabled = true
+        }
+    }
+    if (route === 'cart') {
+        drawCart()
+        drawOrderSuccessful()
+    }
+})