// функция createStore 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 (newState !== state) { state = newState for (let cb of cbs) cb() } } return { dispatch, getState() { return state }, subscribe(cb) { cbs.push(cb) return () => cbs = cbs.filter(c => c !== cb) } } } // функция promiseReducer function promiseReducer(state = {}, { type, status, payload, error, name }) { if (type === 'PROMISE') { return { ...state, [name]: { status, payload, error } } } return state } const store = createStore(promiseReducer) // actions 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 payload = await promise dispatch(actionResolved(name, payload)) return payload } catch(error){ dispatch(actionRejected(name, error)) } } // функция cartReducer function cartReducer(state = {}, { type, count = 1, _id, name }) { if (type === "CART_ADD") { return { ...state, [_id]: { name: name, count: state[_id] ? state[_id].count + count : count } } } if (type === "CART_CHANGE") { return { ...state, [_id]: { name: name, count: count } } } if (type === 'CART_REMOVE') { let { [_id]: count, ...copyWithout } = state return copyWithout } if (type === 'CART_CLEAR') { return {} } return state } // reducers let reducers = { promise: promiseReducer, cart: cartReducer } // функция combineReducers function combineReducers(reducers) { function commonReducer(state = {}, action) { let newState = {} for (let key in reducers) { let innerState = reducers[key](state[key], action) innerState === state[key] ? newState[key] = state[key] : newState[key] = innerState } return newState } return commonReducer } // запросы const getGQL = url => (query, variables = {}) => 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") const actionRootCategories = () => actionPromise('rootCategories', shopGQL(`query cats($query:String) { CategoryFind(query:$query) { _id name } }`, {query: JSON.stringify([{parent:null}])})) store.dispatch(actionRootCategories()) const actionCategoryById = (_id) => actionPromise('catById', shopGQL(`query catById($query:String) { CategoryFindOne(query:$query) { _id name goods { _id name price description images { url } } } }`, {query: JSON.stringify([{_id}])})) const actionGoodById = id => actionPromise('goodById', shopGQL(`query GoodFind($id:String) { GoodFind(query: $id){ name price description images {url} } }`, {id: JSON.stringify([{ "_id": id }])})) 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) { 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(() => console.log(store.getState())) store.subscribe(() => { const { 1: route, 2: id } = location.hash.split('/') if (route === 'categories') { const catById = store.getState().catById?.payload if (catById) { aside.innerText = '' let cats = document.createElement('p') cats.innerText = "Вы в категории ==>" + " " + catById.data.CategoryFindOne.name mainMenu = document.createElement('a') mainMenu.innerText = "Назад в главное меню" mainMenu.onclick = () => { main.innerText = '' drawMainMenu() } aside.append(mainMenu, cats) let goods = catById.data.CategoryFindOne.goods for (let {_id, name, price, description, images} of goods) { let div = document.createElement('div') div.className = 'goods' let btnCart = document.createElement('btn') let btn = document.createElement('button') btn.textContent = "Добавить в карзину" let img = document.createElement('img') img.style.width ='400px' img.src = `http://shop-roles.asmer.fs.a-level.com.ua/${images[0].url}` let prices = document.createElement('span') let descriptions = document.createElement('p') prices.innerText = `Цена: ${price} грн` descriptions.innerText = `Описание: ${description}` let goodA = document.createElement('a') goodA.href = `#/good/${_id}` goodA.innerText = name main.append(div) btnCart.append(btn) div.append(goodA, img, prices, descriptions, btnCart) } } } if (route === 'good') { const goodById = store.getState().goodById?.payload const catById = store.getState().catById?.payload if (goodById) { aside.innerText = '' main.innerText = '' let cats = document.createElement('p') cats.innerText = "Вы в категории ==>" + " " + catById.data.CategoryFindOne.name menuGoods = document.createElement('a') menuGoods.innerText = catById.data.CategoryFindOne.name + " " + "<== Назад в список товаров" mainMenu = document.createElement('a') mainMenu.innerText = "Назад в главное меню" mainMenu.onclick = () => { main.innerText = '' drawMainMenu() } aside.append(mainMenu, cats) let good = goodById.data.GoodFind for (let {name, price, description, images} of good) { let div = document.createElement('div') div.className = 'good' let btn = document.createElement('button') btn.textContent = "Добавить в карзину" let img = document.createElement('img') img.style.width ='400px' img.src = `http://shop-roles.asmer.fs.a-level.com.ua/${images[0].url}` let prices = document.createElement('span') let descriptions = document.createElement('p') prices.innerText = `Цена: ${price} грн` descriptions.innerText = `Описание: ${description}` let goodA = document.createElement('a') goodA.innerText = name main.append(div) div.append(goodA, img, prices, descriptions, btn) } } } })