123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- 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 = `<h1 class="category-name">${name}</h1>`
- 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 = `<h2>${name}</h2>
- <img src="${backendURL}/${images[0].url}" />
- <strong>${price}</strong><br>
- <a href="#/good/${_id}">посмотреть на ${name}</a>
- `
- 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 = `<h1>${name}</h1>`
- const card = document.createElement('div');
- card.innerHTML = `<img src="${backendURL}/${images[0].url}" />
- <strong>${price}</strong><br>
- <div>${description}</div>
- `
- 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 = `<img src="${backendURL}/${good.images[0].url}"/>
- <strong>${good.name}</strong>
- <strong>${count}</strong>
- `
- 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 = `<strong>${good.name}</strong>
- <span>${count} ✖ ${good.price}</span>
- `
- li.append(div);
- }
- li.innerHTML += `<div>${total}</div>
- <div>${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}</div>
- <hr>`
- dashboardUl.append(li)
- }
- }
-
- })
- logout.onclick = () => store.dispatch(actionAuthLogout() )
|