let signin = document.querySelector('#signin'); let register = document.querySelector('#register'); let cartBtn = document.querySelector('#cart-btn'); let logRegisterInput = document.querySelector('#signup'); let passRegisterInput = document.querySelector('#signup-password'); let btnRegister = document.querySelector('#btn-register'); let loginInput = document.querySelector('#login'); let passwordInput = document.querySelector('#login-password'); let btnLogin = document.querySelector('#btn-login'); let overlay = document.querySelector(".overlay"); let registerForm = document.querySelector('#signup-form'); let loginForm = document.querySelector('#login-form'); let cartWrap = document.querySelector('#cart-wrap'); let dashboardWrap = document.querySelector('#dashboard-wrap'); let Closes = document.querySelectorAll(".close"); let registerWrap = document.querySelector("#register-wrap"); let userLogoutWrap = document.querySelector("#userlogout-wrap"); let user = document.querySelector("#user"); let dashboardUl = document.querySelector("#dashboard") let cartUl = document.querySelector("#cart"); let btnBuy = document.querySelector("#btn-buy"); let logout = document.querySelector('#logout'); let dashboardBtn = document.querySelector('#dashboard-btn'); function showAndHideElem(element, value){ element.style.display = value; overlay.style.display = value } signin.addEventListener("click", () => { showAndHideElem(loginForm, 'block'); }); register.addEventListener("click", () => { showAndHideElem(registerForm, 'block'); }) cartBtn.addEventListener("click", () => { showAndHideElem(cartWrap, 'block') }) dashboardBtn.addEventListener("click", () => { showAndHideElem(dashboardWrap, 'block') }) Closes[0].addEventListener("click", () => { showAndHideElem(registerForm, 'none'); registerForm.reset(); }) Closes[1].addEventListener("click", () => { showAndHideElem(loginForm, 'none'); loginForm.reset(); }) Closes[2].addEventListener("click", () => { showAndHideElem(cartWrap, 'none') }) Closes[3].addEventListener("click", () => { showAndHideElem(dashboardWrap, 'none') }) function createStore(reducer){ let state = reducer(undefined, {}) let cbs = [] const getState = () => state const subscribe = cb => (cbs.push(cb), () => cbs = cbs.filter(c => c !== cb)) const dispatch = action => { if (typeof action === 'function'){ return action(dispatch, getState) } const newState = reducer(state, action) if (newState !== state){ state = newState for (let cb of cbs) cb() } } return { getState, dispatch, subscribe } } const getGQL = url => (query, variables) => fetch(url, { method: 'POST', headers: { "Content-Type": "application/json", // 'Accept' : 'application/json', ...(localStorage.authToken ? {"Authorization": "Bearer " + localStorage.authToken} : {}) }, body: JSON.stringify({query, variables}) }).then(res => res.json()) .then(data => { 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'); const jwtDecode = token => { try{ return JSON.parse(atob(token.split('.')[1])); } catch(e){ console.log(e.name, e.message); } } function promiseReducer(state={}, {type, name, status, payload, error}){ if (type === 'PROMISE'){ return { ...state, [name]:{status, payload, error} } } return state } function authReducer(state, {type, token}){ if (state === undefined){ if(localStorage.authToken){ type = 'AUTH_LOGIN'; token = localStorage.authToken } } if(type === 'AUTH_LOGIN'){ let payload = jwtDecode(token); if (payload){ localStorage.authToken = token return {token, payload} } } if(type === 'AUTH_LOGOUT'){ localStorage.removeItem("authToken") return {} } return state || {} } const combineReducers = (reducers) => (state={}, action) => { let newState = {} for (const [reducerName, reducer] of Object.entries(reducers)){ let subNewState = reducer(state[reducerName],action) if(subNewState !== state[reducerName]){ newState = { ...newState, [reducerName] : subNewState } } } if(Object.keys(newState).length > 0){ return { ...state,...newState } } return state } function cartReducer(state = {}, {type, good, count=1}){ //каков state: //{ // _id1: {count:1, good: {_id1, name, price, images}} // _id2: {count:1, good: {_id2, name, price, images}} //} //каковы действия по изменению state if (type === 'CART_ADD'){ return { ...state, [good._id]: {count: count+(state[good._id]?.count || 0), good : good} //копируем старое и подменяем один ключ на новое, однако если //ключ был, берем count из старого и прибавляем к count из action. } } if (type === 'CART_CHANGE'){ return { ...state, [good._id] : {count: count, good : good} // ///!меняем полностью // //копируем старое и подменяем один ключ на новое. аналогично ларьку // //и promiseReducer } } if (type === 'CART_DELETE'){ let {[good._id]: id1, ...newState} = state; return { ...newState } //смочь скопировать объект state без одного ключа //(для этого есть хитрая деструктуризация, например //вернуть копию без этого ключа } 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}) ///oninput меняяем полностью const actionCartDelete = (good) => ({type: 'CART_DELETE', good}) const actionCartClear = () => ({type: 'CART_CLEAR'}) const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer})); store.subscribe(() => console.log(store.getState())) const actionAuthLogin = (token) => ({type: 'AUTH_LOGIN', token}); const actionAuthLogout = () => ({type: 'AUTH_LOGOUT'}); 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 = (name, promise) => async (dispatch) => { dispatch(actionPending(name)) try { let payload = await promise; dispatch(actionFulfilled(name, payload)); return payload } catch(error){ dispatch(actionRejected(name, error)) } } const actionRootCats = () => actionPromise('rootCats', gql(`query { CategoryFind(query: "[{\\"parent\\":null}]"){ _id name } }`)) const actionCatById = (_id) => //добавить подкатегории actionPromise('catById', gql(`query catById($q: String){ CategoryFindOne(query: $q){ _id name subCategories { name _id } goods { _id name price images { url } } } }`, {q: JSON.stringify([{_id}])})) const actionGoodById = (_id) => actionPromise('goodById', gql(`query goodByid($goodId: String) { GoodFindOne(query: $goodId) { name price description images { url } } }`, {goodId: JSON.stringify([{_id}])})) const actionFullRegister = (log, pass) => async dispatch => { let user = await dispatch( actionPromise('register', gql( `mutation register($login: String, $password: String) { UserUpsert(user: {login: $login, password: $password}) { _id login } }`, {login : log, password : pass})) ) if(user){ dispatch(actionFullLogin(log, pass)); } } const actionFullLogin = (log, pass) => async dispatch => { let token = await dispatch( actionPromise('login', gql(`query login($login: String, $password: String) { login(login: $login, password: $password) }`, {login: log, password: pass})) ) if(token){ dispatch(actionAuthLogin(token)) } } const actionNewOrder = () => async (dispatch, getState) => { const {cart} = getState(); let order = {orderGoods : []} for(let [key, value] of Object.entries(cart)){ let newValue = {...value} let {name,price,images, ...id} = newValue.good; newValue.good = id; order.orderGoods.push({...newValue}) } let newOrder = await dispatch( actionPromise('newOrder', gql(`mutation newOrder($order: OrderInput) { OrderUpsert(order: $order) { _id total } }`, {order: order})) ) if(newOrder){ dispatch(actionCartClear()) } } const actionOrders = () => actionPromise('orders', gql(`query findOrder($q: String) { OrderFind(query: $q) { _id total createdAt orderGoods { count good { name price } } } }`, {q: JSON.stringify([{}])})); store.dispatch(actionRootCats()) store.subscribe(() => { const {rootCats} = store.getState().promise if (rootCats?.payload){ aside.innerHTML = '' for (const {_id, name} of rootCats.payload){ const link = document.createElement('a') link.href = `#/category/${_id}` link.innerText = name aside.append(link) } } }) window.onhashchange = () => { const [, route, _id] = location.hash.split('/'); console.log() const routes = { category(){ store.dispatch(actionCatById(_id)); console.log('work') }, good(){ //задиспатчить actionGoodById store.dispatch(actionGoodById(_id)) }, login(){ //отрисовка тут btnLogin.onclick = (e) => {e.preventDefault() ;store.dispatch(actionFullLogin(loginInput.value, passwordInput.value))}; }, register(){ btnRegister.onclick = (e) => {e.preventDefault(); store.dispatch(actionFullRegister(logRegisterInput.value, passRegisterInput.value))} }, dashboard(){ //#/dashboard //задиспатчить actionOrders store.dispatch(actionOrders()) console.log('заказостраница') } } if (route in routes) routes[route]() } window.onhashchange() store.subscribe(() => { const {catById} = store.getState().promise const [,route, _id] = location.hash.split('/') if (catById?.payload && route === 'category'){ const {name, subCategories} = catById.payload main.innerHTML = `

${name}

` if(subCategories){ for(let {name, _id} of subCategories){ const link = document.createElement('a') link.href = `#/category/${_id}` link.innerText = name; main.append(link) } } for (const {_id, name, price, images} of catById.payload.goods){ const card = document.createElement('div') card.innerHTML = `

${name}

${price}
посмотреть на ${name} ` main.append(card); let btnAddToCart = document.createElement('button'); btnAddToCart.classList.add('btn-buy'); btnAddToCart.innerText = 'Добавить в корзину' card.append(btnAddToCart); btnAddToCart.onclick = () => store.dispatch(actionCartAdd({_id: _id, name: name, price: price, images: images})) } } }) store.subscribe(() => { const {goodById} = store.getState().promise const [,route, _id] = location.hash.split('/') if(goodById?.payload && route === 'good'){ const {name, price, description, images} = goodById.payload; main.innerHTML = `

${name}

` const card = document.createElement('div'); card.innerHTML = ` ${price}
${description}
` main.append(card); let btnAddToCart = document.createElement('button'); btnAddToCart.classList.add('btn-buy'); btnAddToCart.innerText = 'Добавить в корзину' card.append(btnAddToCart); btnAddToCart.onclick = () => store.dispatch(actionCartAdd({_id: _id, name: name, price: price, images: images})) } //ТУТ ДОЛЖНА БЫТЬ ПРОВЕРКА НА НАЛИЧИЕ goodById в редакс //и проверка на то, что сейчас в адресной строке адрес ВИДА #/good/АЙДИ //в таком случае очищаем main и рисуем информацию про товар с подробностями //....А ТАК ЖЕ КНОПКА Купить, которая диспатчит actionCartAdd }) store.subscribe(() => { //если залогинен отрисовать юзернейм и кнопку логаут const {payload} = store.getState().auth if(payload?.sub){ registerWrap.style.display = 'none'; userLogoutWrap.style.display = 'block'; const {login} = payload.sub; user.innerHTML = login; } else { registerWrap.style.display = 'block'; userLogoutWrap.style.display = 'none'; } }) store.subscribe(() => { cartUl.innerHTML = '' const {cart} = store.getState(); for (let value of Object.values(cart)){ const {count, good} = value; const li = document.createElement("li"); li.innerHTML = ` ${good.name} ${count} ` cartUl.append(li); const input = document.createElement("input"); li.append(input); input.value = count input.oninput = () => store.dispatch(actionCartChange(good, +input.value)); const button = document.createElement("button"); button.innerText = 'удалить'; li.append(button); button.onclick = () => store.dispatch(actionCartDelete(good)) } }) store.subscribe(() => { const {cart} = store.getState(); Object.keys(cart).length > 0 ? btnBuy.style.display='block' : btnBuy.style.display='none'; btnBuy.onclick = () => { store.dispatch(actionNewOrder()); } }) store.subscribe(() => { dashboardUl.innerHTML = '' const {orders} = store.getState().promise; const [,route, _id] = location.hash.split('/'); if(orders?.payload && route === 'dashboard'){ for(let {createdAt, total, orderGoods} of orders.payload){ let date = new Date(+createdAt); let li = document.createElement("li"); for(let {count, good} of orderGoods){ let div = document.createElement("div"); div.innerHTML = `${good.name} ${count} ✖ ${good.price} ` li.append(div); } li.innerHTML += `
${total}
${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}

` dashboardUl.append(li) } } }) logout.onclick = () => store.dispatch(actionAuthLogout() )