let aside = document.querySelector(".aside"); let content = document.querySelector(".main__main-content"); let registration = document.querySelector(".header__registration"); let signIn = document.querySelector(".header__sign-in"); let cart = document.querySelector(".imaginary-shopping-cart--content"); let cartButton = document.querySelector(".header__cart"); let dasboard = document.querySelector(".header__dashboard"); registration.addEventListener("click", function() { location.href = "#/register"; }); signIn.addEventListener("click", function() { location.href = "#/login"; }); cartButton.addEventListener("click", function() { location.href = "#/cart"; }); dasboard.addEventListener("click", function() { location.href = "#/dasboard"; }); let createStore = function(reducer) { let state = reducer(undefined, {}); let cbs = []; let getState = () => state; let subscribe = function(cb) { cbs.push(cb); return () => cbs = cbs.filter(c => c !== cb); }; let dispatch = function(action) { if(typeof(action) == "function") { return action(dispatch, getState); }; let newState = reducer(state, action); if (newState !== state){ state = newState; for (let cb of cbs) { cb(); }; }; }; return { getState, subscribe, dispatch }; }; const promiseReducer = function(state={}, {type, name, status, payload, error}) { if (type == 'PROMISE'){ return { ...state, [name]:{status, payload, error} } } return state; }; const actionPending = name => ({type: "PROMISE", name, status: 'PENDING'}); const actionFulfilled = (name,payload) => ({type: "PROMISE", name, status: 'FULFILLED', payload}); const actionRejected = (name,error) => ({type: "PROMISE", name, status: 'REJECTED', error}); const actionPromise = function(name, promise) { return async dispatch => { dispatch(actionPending(name)); try { let payload = await promise dispatch(actionFulfilled(name, payload)) return payload } catch(error){ dispatch(actionRejected(name, error)) }; }; }; let jwtDecode = function(token) { let payloadInBase64; let payloadInJson; let payload; try { payloadInBase64 = token.split(".")[1]; payloadInJson = atob(payloadInBase64); payload = JSON.parse(payloadInJson); return payload; } catch(err) { } }; const authReducer = function(state, {type, token}) { let payload; if (state == undefined) { if(localStorage.authToken) { type = "AUTH_LOGIN"; token = localStorage.authToken; } else { type = "AUTH_LOGOUT"; }; }; if (type == "AUTH_LOGIN") { payload = jwtDecode(token); if(payload) { localStorage.authToken = token; return { token: token, payload: payload } } }; if (type == "AUTH_LOGOUT") { localStorage.removeItem("authToken"); return {}; }; return state || {}; }; const actionAuthLogin = token => ({type: "AUTH_LOGIN", token}); const actionAuthLogout = () => ({type: "AUTH_LOGOUT"}); let cartReducer = function(state={}, {type, good, count=1}) { if(type == "CART_ADD") { let newState = {...state}; if(good["_id"] in state) { newState[good._id].count = newState[good._id].count + count; } else { newState = { ...state, [good._id]: {count, good} }; }; return newState; }; if(type == "CART_CHANGE") { let newState = {...state, [good._id]: {count, good} }; return newState; }; if(type == "CART_DELETE") { let newState = {...state}; delete newState[good._id]; return newState; }; if(type == "CART_CLEAR") { return {}; }; return state; }; const actionCartAdd = (good, count=1) => ({type: 'CART_ADD', good, count}) const actionCartChange = (good, count=1) => ({type: 'CART_CHANGE', good, count}) const actionCartDelete = (good) => ({type: 'CART_DELETE', good}) const actionCartClear = () => ({type: 'CART_CLEAR'}) const combineReducers = function(reducers) { return function (state={}, action) { const newState = {}; for(let [reducerName, reducer] of Object.entries(reducers)) { let checkState = reducer(state[reducerName], action); if(state[reducerName] != checkState) { newState[reducerName] = checkState; }; }; if(Object.keys(newState).length == 0) { return state; }; return {...Object.assign(state, newState)}; }; }; const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer})); store.subscribe(() => console.log(store.getState())); const getGQL = function(url) { return async function(query, variables) { const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", ...(localStorage.authToken ? { "Authorization": "Bearer " + localStorage.authToken } : {}) }, body: JSON.stringify({ query, variables }) }); const data = await res.json(); if (data.data) { return Object.values(data.data)[0]; } else { throw new Error(JSON.stringify(data.errors)); }; }; }; const backendURL = 'http://shop-roles.asmer.fs.a-level.com.ua'; const gql = getGQL(backendURL + '/graphql'); let actionRootCats = function() { return actionPromise("rootCats", gql(`query { CategoryFind(query: "[{\\"parent\\":null}]"){ _id name } }`)); }; store.dispatch(actionRootCats()); let actionCatById = function(_id) { return actionPromise("catById", gql(`query catById($q: String){ CategoryFindOne(query: $q){ _id name goods { _id name price images { url } } } }`, {q: JSON.stringify([{_id}])} )); }; let actionGoodById = function(_id) { return actionPromise("goodById", gql(`query findGood($goodQuery: String) { GoodFindOne(query:$goodQuery) { _id name price images { url } } }`, {goodQuery: JSON.stringify([{"_id": _id}])} )); }; let actionFullLogin = async function(login, password) { let token = await gql("query userLogin($login: String, $password: String) {login(login: $login, password: $password)}", {"login": login, "password": password}); store.dispatch(actionAuthLogin(token)); }; let actionFullRegister = function(login, password, nick) { return actionPromise("userRegister", gql(`mutation userRegister($login:String, $password:String, $nick:String) { UserUpsert(user: {login:$login, password:$password, nick:$nick}) { _id login nick } }`, { "login": login, "password": password, "nick": nick })) }; let actionOrders = async function() { let order = await gql(`mutation makeOrder($order:OrderInput){ OrderUpsert(order: $order){ _id } }`, { "order": { orderGoods: Object.entries(store.getState().cart).map(([_id, count]) =>({"count": count.count, "good": {_id}})) } }); store.dispatch(actionCartClear()); } let createCart = function() { const [,route, _id] = location.hash.split('/'); if(route == "cart") { let str = "

Корзина

"; for(let goodId of Object.entries(store.getState().cart)) { str += ``; }; str += "
${goodId[0]} ${goodId[1].good.name} Цена: ${goodId[1].good.price} Кол-во:
"; content.innerHTML = str; table.addEventListener("click", function(evt) { if(evt.target.tagName == "BUTTON") { store.dispatch(actionCartDelete(store.getState().cart[evt.path[2].firstElementChild.textContent]?.good)) }; }); order.addEventListener("click", function() { actionOrders(); }); } }; let createDashbord = async function() { const [,route, _id] = location.hash.split('/'); if(route == "dasboard") { let orders = await gql(`query ordersFind($query:String) { OrderFind(query: $query) { createdAt orderGoods { count good { name price images { url } } } } }`, { "query": JSON.stringify([{}]) }); let str = "

История заказов

"; for(let order of orders) { str += ""; str += ``; str += "" str += ""; }; str += "
${(new Date(+order.createdAt)).toLocaleString()}" for(let orderGood of order.orderGoods) { str += `
  • ${orderGood.good.name}
  • Цена: ${orderGood.good.price}
`; } str += "
"; content.innerHTML = str; } }; window.onhashchange = () => { const [, route, _id] = location.hash.split('/'); const routes = { category(){ store.dispatch(actionCatById(_id)); }, good(){ store.dispatch(actionGoodById(_id)); }, login(){ content.innerHTML = `

Вход на сайт

`; sign_in.addEventListener("click", function() { actionFullLogin(login.value, password.value); // store.dispatch(actionFullLogin(login.value, password.value)); }); }, register(){ content.innerHTML = `

Регистрация

`; registr.addEventListener("click", function() { store.dispatch(actionFullRegister(login.value, password.value, nick.value)); }); }, cart() { createCart(); }, dasboard() { createDashbord(); } }; if (route in routes) { routes[route](); }; }; window.onhashchange(); store.subscribe(() => { const {rootCats} = store.getState().promise; if (rootCats?.payload){ let ul = document.createElement("ul"); aside.innerHTML = ''; for (const {_id, name} of rootCats?.payload){ let li = document.createElement("li"); let link = document.createElement('a'); link.href = `#/category/${_id}`; link.innerText = name; li.append(link); ul.append(li); } aside.append(ul); }; }); store.subscribe(() => { const {catById} = store.getState().promise; const [,route, _id] = location.hash.split('/') if (catById?.payload && route === 'category'){ const {name} = catById.payload content.innerHTML = `

${name}

ТУТ ДОЛЖНЫ БЫТЬ ПОДКАТЕГОРИИ` for (const {_id, name, price, images} of catById.payload.goods){ const card = document.createElement('div') card.innerHTML = `

${name}

${price} ССЫЛКА НА СТРАНИЦУ ТОВАРА` content.append(card); } } }); store.subscribe(() => { const {goodById} = store.getState().promise; const [,route, _id] = location.hash.split('/'); if (goodById?.payload && route == "good") { const {name} = goodById.payload; content.innerHTML = `

${name}

`; const card = document.createElement('div'); card.innerHTML = `

${goodById.payload.name}

${goodById.payload.price} `; content.append(card); goodAdd.addEventListener("click", function() { store.dispatch(actionCartAdd(goodById.payload)); }); }; }); store.subscribe(createCart);