|
@@ -0,0 +1,203 @@
|
|
|
|
+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 => {
|
|
|
|
+ return function(query, variables={}) {
|
|
|
|
+ return fetch(url,
|
|
|
|
+ {
|
|
|
|
+ method: "POST",
|
|
|
|
+ headers:
|
|
|
|
+ {"Content-Type": "application/json",
|
|
|
|
+ ...(localStorage.authToken ? {Authorization: localStorage.authToken} : {})
|
|
|
|
+ },
|
|
|
|
+ body: JSON.stringify({query, variables})
|
|
|
|
+ }).then(resp => resp.json())
|
|
|
|
+ // .then(data => {
|
|
|
|
+ // if ("errors" in data) {
|
|
|
|
+ // throw new 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) {
|
|
|
|
+ name description price images {
|
|
|
|
+ _id text url
|
|
|
|
+ }
|
|
|
|
+ categories {
|
|
|
|
+ _id name
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }`, { id })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+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 categoryName = document.createElement('div')
|
|
|
|
+ categoryName.innerText = catById.data.CategoryFindOne.name
|
|
|
|
+ categoryName.style.fontSize = '25px'
|
|
|
|
+ categoryName.style.fontWeight = 'bold'
|
|
|
|
+ main.append(categoryName)
|
|
|
|
+ for (let {_id, name} of catById.data.CategoryFindOne.goods){
|
|
|
|
+ let good = document.createElement('a')
|
|
|
|
+ good.href = `#/good/${_id}`
|
|
|
|
+ good.innerText = name
|
|
|
|
+
|
|
|
|
+ let btn = document.createElement('button')
|
|
|
|
+ btn.style.cursor = 'pointer'
|
|
|
|
+ btn.innerText = 'купыть'
|
|
|
|
+ main.append(good, btn)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (route === 'good'){
|
|
|
|
+ const goodById = store.getState().goodById?.payload
|
|
|
|
+ if (goodById){
|
|
|
|
+ main.innerText = ''
|
|
|
|
+ let {name, description, price} = goodById.data.GoodFindOne
|
|
|
|
+ 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.style.cursor = 'pointer'
|
|
|
|
+ btn.innerText = 'купыть'
|
|
|
|
+
|
|
|
|
+ main.append(goodName, goodDescription, goodPrice, btn)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+})
|