|
@@ -0,0 +1,269 @@
|
|
|
|
+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 promiseReducer(state={}, {type, status, payload, error, name}){
|
|
|
|
+ if (type === 'PROMISE'){
|
|
|
|
+ return {
|
|
|
|
+ ...state,
|
|
|
|
+ [name]:{status, payload, error}
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return state
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+//под товаром сделать кнопку "купить"
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+const store = createStore(promiseReducer)
|
|
|
|
+const unsubscribe1 = store.subscribe(() => console.log(store.getState()))
|
|
|
|
+
|
|
|
|
+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 =>
|
|
|
|
+ (query, variables={}) => fetch(url, {
|
|
|
|
+ method: 'POST',
|
|
|
|
+ headers: {
|
|
|
|
+ // Accept: "application/json",
|
|
|
|
+ "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 goodById = id => {
|
|
|
|
+ let query = `query goodById($query:String) {
|
|
|
|
+ GoodFindOne(query: $query ) {
|
|
|
|
+ _id
|
|
|
|
+ name
|
|
|
|
+ description
|
|
|
|
+ price
|
|
|
|
+ images {
|
|
|
|
+ url
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }`
|
|
|
|
+ let variables = {
|
|
|
|
+ query: JSON.stringify([{_id: id}])
|
|
|
|
+ }
|
|
|
|
+ let res = shopGQL(query,variables)
|
|
|
|
+ return res
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const actionGoodById = id =>
|
|
|
|
+ actionPromise('goodById', goodById(id))
|
|
|
|
+
|
|
|
|
+const actionRootCategories = () =>
|
|
|
|
+ actionPromise('rootCategories', shopGQL(`
|
|
|
|
+ query cats($query:String){
|
|
|
|
+ CategoryFind(query:$query){
|
|
|
|
+ _id name
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ `, {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
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }`, {query: JSON.stringify([{_id}])}))
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+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))
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function drawMainMenu(){
|
|
|
|
+ let cats = store.getState().rootCategories.payload
|
|
|
|
+ if (cats){ //каждый раз дорисовываются в body
|
|
|
|
+ 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(() => {
|
|
|
|
+ const {1: route, 2:id} = location.hash.split('/')
|
|
|
|
+ if (route === 'categories'){
|
|
|
|
+ const catById = store.getState().catById?.payload
|
|
|
|
+ if (catById){
|
|
|
|
+ main.innerText = ''
|
|
|
|
+ let h = document.createElement('h2')
|
|
|
|
+ h.style.fontSize = '30px'
|
|
|
|
+ h.style.marginTop = 0
|
|
|
|
+ h.innerHTML = catById.data.CategoryFindOne.name
|
|
|
|
+ h.style.textAlign = 'center'
|
|
|
|
+ main.append(h)
|
|
|
|
+ //вывести циклом товары со ссылками вида #/good/АЙДИШНИК
|
|
|
|
+ let goods = document.createElement('div')
|
|
|
|
+ goods.className = 'goods'
|
|
|
|
+ for (let key in catById.data.CategoryFindOne.goods) {
|
|
|
|
+ let box = document.createElement('div')
|
|
|
|
+ box.style.border = '3px solid #008B8B'
|
|
|
|
+ box.style.padding = '10px'
|
|
|
|
+ box.style.margin = '20px'
|
|
|
|
+ let img = document.createElement('img')
|
|
|
|
+ let productName = document.createElement('h3')
|
|
|
|
+ let a = document.createElement('a')
|
|
|
|
+ let price = document.createElement('p')
|
|
|
|
+ let description = document.createElement('p')
|
|
|
|
+ let btnBuy = document.createElement('button')
|
|
|
|
+ btnBuy.innerHTML = "Купить"
|
|
|
|
+ btnBuy.style.backgroundColor = '#ADFF2F'
|
|
|
|
+ btnBuy.style.display = 'block'
|
|
|
|
+ btnBuy.style.marginLeft = 'auto'
|
|
|
|
+ btnBuy.style.marginRight = 'auto'
|
|
|
|
+ btnBuy.style.marginBottom = '10px'
|
|
|
|
+ let btnAdd = document.createElement('button')
|
|
|
|
+ btnAdd.innerHTML = "Добавить в корзину"
|
|
|
|
+ btnAdd.style.backgroundColor = '#3CB371'
|
|
|
|
+ btnAdd.style.display = 'block'
|
|
|
|
+ btnAdd.style.marginLeft = 'auto'
|
|
|
|
+ btnAdd.style.marginRight = 'auto'
|
|
|
|
+ let btnMore = document.createElement('button')
|
|
|
|
+ btnMore.innerHTML = "Подробнее"
|
|
|
|
+ btnMore.style.backgroundColor = '#DCDCDC'
|
|
|
|
+ btnMore.style.display = 'block'
|
|
|
|
+ btnMore.style.marginLeft = 'auto'
|
|
|
|
+ btnMore.style.marginRight = 'auto'
|
|
|
|
+ btnMore.style.marginTop = '20px'
|
|
|
|
+ img.src = 'http://shop-roles.asmer.fs.a-level.com.ua/' + catById.data.CategoryFindOne.goods[key].images[0].url
|
|
|
|
+ img.style.width = '300px'
|
|
|
|
+ img.style.display = 'block'
|
|
|
|
+ img.style.marginLeft = 'auto'
|
|
|
|
+ img.style.marginRight = 'auto'
|
|
|
|
+ a.href = '#/good/' + catById.data.CategoryFindOne.goods[key]._id
|
|
|
|
+ productName.innerHTML = catById.data.CategoryFindOne.goods[key].name + '<br/>'
|
|
|
|
+ price.innerHTML = "Цена:" + " " + catById.data.CategoryFindOne.goods[key].price + ' ' + 'uah'
|
|
|
|
+ a.style.textDecoration = 'none'
|
|
|
|
+ a.style.color = 'black'
|
|
|
|
+ description.innerHTML = catById.data.CategoryFindOne.goods[key].description
|
|
|
|
+ description.style.textAlign = 'center'
|
|
|
|
+ a.append(btnMore)
|
|
|
|
+ box.append(productName)
|
|
|
|
+ box.append(price)
|
|
|
|
+ box.append(img)
|
|
|
|
+ box.append(description)
|
|
|
|
+ box.append(btnBuy)
|
|
|
|
+ box.append(btnAdd)
|
|
|
|
+ box.append(a)
|
|
|
|
+ goods.append(box)
|
|
|
|
+ }
|
|
|
|
+ main.append(goods)
|
|
|
|
+ //ПРИДУМАТЬ КНОПКИ ДОБАВЛЕНИЯ В КОРЗИНУ
|
|
|
|
+ // main.innerHTML = `<pre>${JSON.stringify(catById, null ,4)}</pre>`
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (route === 'good'){
|
|
|
|
+ const goodById = store.getState().goodById?.payload
|
|
|
|
+ if (goodById){ //вывести в main страницу товара
|
|
|
|
+ main.innerText = " "
|
|
|
|
+ let source = goodById.data.GoodFindOne
|
|
|
|
+ let product = document.createElement('div')
|
|
|
|
+ let page = document.createElement('div')
|
|
|
|
+ let h = document.createElement('h1')
|
|
|
|
+ h.innerHTML = source.name
|
|
|
|
+ h.style.textAlign = 'center'
|
|
|
|
+ let img = document.createElement('img')
|
|
|
|
+ img.src = 'http://shop-roles.asmer.fs.a-level.com.ua/' + source.images[0].url
|
|
|
|
+ img.style.width = '300px'
|
|
|
|
+ img.style.display = 'block'
|
|
|
|
+ img.style.marginLeft = 'auto'
|
|
|
|
+ img.style.marginRight = 'auto'
|
|
|
|
+ let description = document.createElement('p')
|
|
|
|
+ description.innerHTML = source.description
|
|
|
|
+ description.style.textAlign = 'center'
|
|
|
|
+ let price = document.createElement('p')
|
|
|
|
+ price.innerHTML = 'Цена:' + " " + source.price + 'uah'
|
|
|
|
+ price.textAlign = 'center'
|
|
|
|
+ let btnBuy = document.createElement('button')
|
|
|
|
+ btnBuy.innerHTML = "Купить"
|
|
|
|
+ btnBuy.style.backgroundColor = '#ADFF2F'
|
|
|
|
+ btnBuy.style.display = 'block'
|
|
|
|
+ btnBuy.style.marginLeft = 'auto'
|
|
|
|
+ btnBuy.style.marginRight = 'auto'
|
|
|
|
+ btnBuy.style.marginBottom = '10px'
|
|
|
|
+ btnBuy.style.marginTop = '50px'
|
|
|
|
+ btnBuy.style.width = '300px'
|
|
|
|
+ btnBuy.style.fontSize = '20px'
|
|
|
|
+ let btnAdd = document.createElement('button')
|
|
|
|
+ btnAdd.innerHTML = "Добавить в корзину"
|
|
|
|
+ btnAdd.style.backgroundColor = '#3CB371'
|
|
|
|
+ btnAdd.style.display = 'block'
|
|
|
|
+ btnAdd.style.marginLeft = 'auto'
|
|
|
|
+ btnAdd.style.marginRight = 'auto'
|
|
|
|
+ btnAdd.style.width = '300px'
|
|
|
|
+ btnAdd.style.fontSize = '20px'
|
|
|
|
+ page.append(h)
|
|
|
|
+ page.append(img)
|
|
|
|
+ page.append(description)
|
|
|
|
+ page.append(price)
|
|
|
|
+ page.append(btnBuy)
|
|
|
|
+ page.append(btnAdd)
|
|
|
|
+ product.append(page)
|
|
|
|
+ main.append(product)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+})
|