|
@@ -0,0 +1,280 @@
|
|
|
|
+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 (query, variables) => {
|
|
|
|
+ return 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')
|
|
|
|
+
|
|
|
|
+let categoryById = async (id) => {
|
|
|
|
+ let query = `query fndcategory($id: String) {
|
|
|
|
+ CategoryFind(query: $id){
|
|
|
|
+ name goods{
|
|
|
|
+ _id name price images {
|
|
|
|
+ url
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }`
|
|
|
|
+
|
|
|
|
+ let qVariables = {
|
|
|
|
+ "id": JSON.stringify([{ "_id": id }])
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let res = await shopGQL(query, qVariables)
|
|
|
|
+ console.log(res)
|
|
|
|
+ return res
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+let goodById = async (id) => {
|
|
|
|
+ let query = `query fndgood($id: String) {
|
|
|
|
+ GoodFind(query: $id){
|
|
|
|
+ name description price images {
|
|
|
|
+ url
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }`
|
|
|
|
+
|
|
|
|
+ let qVariables = {
|
|
|
|
+ "id": JSON.stringify([{ "_id": id }])
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let res = await shopGQL(query, qVariables)
|
|
|
|
+ return res
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+let newOrder = async (obj) => {
|
|
|
|
+ let option = Object.entries(obj);
|
|
|
|
+ let orderGoods = [];
|
|
|
|
+
|
|
|
|
+ for (let key of option) {
|
|
|
|
+ let i = {
|
|
|
|
+ "count": key[1],
|
|
|
|
+ "good": { "_id": key[0] }
|
|
|
|
+ }
|
|
|
|
+ orderGoods.push(i);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let query = `mutation sndOrder($order: OrderInput) {
|
|
|
|
+ OrderUpsert(order: $order) {
|
|
|
|
+ _id createdAt total
|
|
|
|
+ }
|
|
|
|
+ }`
|
|
|
|
+
|
|
|
|
+ let qVariables = {
|
|
|
|
+ "order": {
|
|
|
|
+ "orderGoods": orderGoods
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let res = await shopGQL(query, qVariables)
|
|
|
|
+ console.log(res)
|
|
|
|
+ return res
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const actionRootCategories = () =>
|
|
|
|
+ actionPromise('rootCategories', shopGQL(`
|
|
|
|
+ query cats($query:String){
|
|
|
|
+ CategoryFind(query:$query){
|
|
|
|
+ _id name
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ `, { query: JSON.stringify([{ parent: null }]) }))
|
|
|
|
+
|
|
|
|
+const actionCategoryById = id => actionPromise('catById', categoryById(id))
|
|
|
|
+
|
|
|
|
+const actionGoodById = id => actionPromise('goodById', goodById(id))
|
|
|
|
+
|
|
|
|
+const actionBuyGood = (obj) => actionPromise('newOrder', newOrder(obj))
|
|
|
|
+
|
|
|
|
+// const actionGoodOrder = (obj) => actionPromise('newOrderGood', newOrderGood(obj))
|
|
|
|
+
|
|
|
|
+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) {
|
|
|
|
+ while (main.lastChild) {
|
|
|
|
+ main.lastChild.remove()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let categoryName = document.createElement("h2")
|
|
|
|
+ let cards = document.createElement("div")
|
|
|
|
+ cards.className = "cards"
|
|
|
|
+ categoryName.textContent = catById.data.CategoryFind[0].name
|
|
|
|
+
|
|
|
|
+ for (let key of catById.data.CategoryFind[0].goods) {
|
|
|
|
+ cardDraw(key, cards)
|
|
|
|
+ }
|
|
|
|
+ main.append(categoryName, cards)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (route === 'good') {
|
|
|
|
+ const goodById = store.getState().goodById?.payload
|
|
|
|
+ if (goodById) { //вывести в main страницу товара
|
|
|
|
+ while (main.lastChild) {
|
|
|
|
+ main.lastChild.remove()
|
|
|
|
+ }
|
|
|
|
+ goodDraw(goodById.data.GoodFind[0], main)
|
|
|
|
+ let count = document.createElement("input")
|
|
|
|
+ count.type = "number"
|
|
|
|
+ count.min = 1
|
|
|
|
+ count.value = 1
|
|
|
|
+ let goodBtnBox = document.createElement("div")
|
|
|
|
+ let btn1 = document.createElement("button")
|
|
|
|
+ let btn2 = document.createElement("button")
|
|
|
|
+ goodBtnBox.style.display = "flex"
|
|
|
|
+ goodBtnBox.style.alignItems = "center"
|
|
|
|
+ btn1.textContent = "Купить"
|
|
|
|
+ btn2.textContent = "В корзину"
|
|
|
|
+ goodBtnBox.append(count, btn2, btn1)
|
|
|
|
+ main.lastChild.append(goodBtnBox)
|
|
|
|
+ let value = 1;
|
|
|
|
+
|
|
|
|
+ count.oninput = () => value = +count.value
|
|
|
|
+
|
|
|
|
+ let order = {
|
|
|
|
+ [id]: value
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ btn1.onclick = () => {
|
|
|
|
+ store.dispatch(actionBuyGood(order))
|
|
|
|
+ }
|
|
|
|
+ /* btn2.onclick = () => {
|
|
|
|
+ store.dispatch(actionGoodOrder(order))
|
|
|
|
+ } */
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+let goodDraw = (obj, parent) => {
|
|
|
|
+ let box = document.createElement("div")
|
|
|
|
+ let goodName = document.createElement("h2")
|
|
|
|
+ let goodIMG = document.createElement("img")
|
|
|
|
+ let description = document.createElement("p")
|
|
|
|
+ let price = document.createElement("span")
|
|
|
|
+
|
|
|
|
+ price.textContent = "Цена: " + obj.price + "грн"
|
|
|
|
+ goodIMG.src = "http://shop-roles.asmer.fs.a-level.com.ua/" + obj.images[0].url
|
|
|
|
+ box.style.width = "40%"
|
|
|
|
+ box.style.margin = "20px"
|
|
|
|
+ box.style.display = "flex"
|
|
|
|
+ box.style.flexDirection = "column"
|
|
|
|
+ goodName.textContent = obj.name
|
|
|
|
+ description.textContent = obj.description
|
|
|
|
+
|
|
|
|
+ box.append(goodIMG, goodName, description, price)
|
|
|
|
+ parent.append(box)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+let cardDraw = (obj, parent) => {
|
|
|
|
+ let box = document.createElement("div");
|
|
|
|
+ let img = document.createElement("img");
|
|
|
|
+ let price = document.createElement("span");
|
|
|
|
+ let productName = document.createElement("h5");
|
|
|
|
+
|
|
|
|
+ img.src = "http://shop-roles.asmer.fs.a-level.com.ua/" + obj.images[0].url;
|
|
|
|
+ productName.textContent = obj.name
|
|
|
|
+ price.textContent = "Цена: " + obj.price + " грн"
|
|
|
|
+
|
|
|
|
+ let productBody = document.createElement("div")
|
|
|
|
+ box.className = "card"
|
|
|
|
+ productBody.append(productName, price)
|
|
|
|
+ box.append(img, productBody)
|
|
|
|
+ let a = document.createElement("a")
|
|
|
|
+ a.href = "#/good/" + obj._id
|
|
|
|
+ a.append(box)
|
|
|
|
+ parent.append(a)
|
|
|
|
+}
|