Aleksandrov96 пре 2 година
родитељ
комит
9f09829958
2 измењених фајлова са 232 додато и 0 уклоњено
  1. 33 0
      homeWork15ReduxGraphQL/index.html
  2. 199 0
      homeWork15ReduxGraphQL/index.js

+ 33 - 0
homeWork15ReduxGraphQL/index.html

@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+    <head>
+        <title>GQL</title>
+        <meta charset='utf8' />
+        <style>
+            #mainContainer {
+                display: flex;
+            }
+            #aside {
+                width: 30%;
+            }
+            #aside > a{
+                display: block;
+            }
+            #main > a{
+                display: block;
+            }
+        </style>
+    </head>
+    <body>
+        <header>КУДА Я ПОПАЛ?</header>
+        <div id='mainContainer'>
+            <aside id='aside'>
+                Категории
+            </aside>
+            <main id='main'>
+                Контент
+            </main>
+        </div>
+        <script src='index.js'></script>
+    </body>
+</html>

+ 199 - 0
homeWork15ReduxGraphQL/index.js

@@ -0,0 +1,199 @@
+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){
+            _id name goods {
+                _id name price images {
+                    url
+                }
+            }
+            subCategories {
+                _id name 
+            }
+        }
+    }`, {q: JSON.stringify([{_id}])}))
+
+//actionGoodById по аналогии
+
+const actionGoodById = (_id) => 
+    actionPromise('goodById', gql(`query goodById($q: String) {
+        GoodFindOne(query: $q) {
+            _id name price description 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))
+            console.log('ТОВАРОСТРАНИЦА')                
+        },
+    }
+    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} = catById.payload;
+        main.innerHTML =    `<h1>${name}</h1>`
+      
+        if (catById.payload.subCategories) {
+            for(const {_id, name} of catById.payload.subCategories) {
+                const link      = document.createElement('a');
+                link.href       = `#/category/${_id}`;
+                link.innerText  = name;
+                main.append(link);
+            }
+        }
+        
+        if (catById.payload.goods) {
+            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>
+                                  <br>
+                                  <a href="#/good/${_id}">Перейти на страницу товара</a>
+                                    `
+                main.append(card)
+            }
+        }
+    }
+})
+
+store.subscribe(() => {
+    const {goodById} = store.getState();
+    const [,route, _id] = location.hash.split('/');
+    if (goodById?.payload && route === 'good') {
+        main.innerHTML = '';
+        const {_id, name, images, price, description}  = goodById.payload;
+            const card = document.createElement('div');
+            card.innerHTML = `<h2>${name}</h2>
+                            <img src="${backURL}/${images[0].url}" />
+                            <strong>${price}</strong>
+                            <h2>${description}</h2>
+                            `;
+            main.append(card);
+        }
+    }
+    //ТУТ ДОЛЖНА БЫТЬ ПРОВЕРКА НА НАЛИЧИЕ goodById в редакс
+    //и проверка на то, что сейчас в адресной строке адрес ВИДА #/good/АЙДИ
+    //в таком случае очищаем main и рисуем информацию про товар с подробностями
+)
+
+
+
+//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())))
+