// url of backend const url = 'http://shop-roles.node.ed.asmer.org.ua/graphql' // Вспомогательные функции ======================================================================================================== // getGql - переделка из HW18 функции gql (делаем запрос на бэк) function getGql(endpoint) { return async function gql(query, variables = {}) { let headers = { 'Content-Type': 'application/json;charset=utf-8', 'Accept': 'application/json', } if (('authToken' in localStorage)) { headers.Authorization = 'Bearer ' + localStorage.authToken } let result = await fetch(endpoint, { method: 'POST', headers, body: JSON.stringify({ query, variables }) }).then(res => res.json()) if (('errors' in result) && !('data' in result)) { throw new Error(JSON.stringify(result.errors)) } result = Object.values(result.data)[0] return result } } const gql = getGql(url) //========================================================================================================= // localStoredReducer function localStoredReducer(originalReducer, localStorageKey) { function wrapper(state, action) { if (!state) { try { return JSON.parse(localStorage[localStorageKey]) } catch (error) { } } const newState = originalReducer(state, action) localStorage[localStorageKey] = JSON.stringify(newState) return newState } return wrapper } // Redux ======================================================================================================== // createStore function createStore(reducer) { let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined let cbs = [] //массив подписчиков const getState = () => state //функция, возвращающая переменную из замыкания const subscribe = cb => (cbs.push(cb), //запоминаем подписчиков в массиве () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка const dispatch = action => { if (typeof action === 'function') { //если action - не объект, а функция return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы } const newState = reducer(state, action) //пробуем запустить редьюсер if (newState !== state) { //проверяем, смог ли редьюсер обработать action state = newState //если смог, то обновляем state for (let cb of cbs) { cb() } //и запускаем подписчиков } } return { getState, dispatch, subscribe } } // создание combineReducers function combineReducers(reducers) { function totalReducer(state = {}, action) { const newTotalState = {} for (const [reducerName, reducer] of Object.entries(reducers)) { const newSubState = reducer(state[reducerName], action) if (newSubState !== state[reducerName]) { newTotalState[reducerName] = newSubState } } if (Object.keys(newTotalState).length) { return { ...state, ...newTotalState } } return state } return totalReducer } // создание promiseReducer function promiseReducer(state = {}, { type, status, payload, error, nameOfPromise }) { if (type === 'PROMISE') { return { ...state, [nameOfPromise]: { status, payload, error } } } return state } // акшоны для promiseReducer const actionPending = nameOfPromise => ({ nameOfPromise, type: 'PROMISE', status: 'PENDING' }) const actionFulfilled = (nameOfPromise, payload) => ({ nameOfPromise, type: 'PROMISE', status: 'FULFILLED', payload }) const actionRejected = (nameOfPromise, error) => ({ nameOfPromise, type: 'PROMISE', status: 'REJECTED', error }) const actionPromise = (nameOfPromise, promise) => async dispatch => { dispatch(actionPending(nameOfPromise)) //сигнализируем redux, что промис начался try { const payload = await promise //ожидаем промиса dispatch(actionFulfilled(nameOfPromise, payload)) //сигнализируем redux, что промис успешно выполнен return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса } catch (error) { dispatch(actionRejected(nameOfPromise, error)) //в случае ошибки - сигнализируем redux, что промис несложился } } // authReducer // раскодируем JWT-токен const jwtDecode = function (token) { try { let parseData = token.split('.')[1] return JSON.parse(atob(parseData)) } catch (e) { return undefined } } function authReducer(state = {}, { type, token }) { if (type === 'AUTH_LOGIN') { let payload = jwtDecode(token) return state = { token, payload } } if (type === 'AUTH_LOGOUT') { localStorage.removeItem('authToken') return {} } return state } // акшон для логинизации const actionAuthLogin = token => ({ type: 'AUTH_LOGIN', token }) // акшон для раззлогинивания const actionAuthLogout = () => ({ type: 'AUTH_LOGOUT' }) // cartReducer function cartReducer(state = {}, { type, count, good }) { if (type === 'CART_ADD') { return { ...state, [good._id]: { good, count: (state[good._id] ? state[good._id].count + count : count) } } } if (type === 'CART_SUB') { if (state[good._id]) { let newCount = state[good._id].count - count if (newCount > 0) { return { ...state, [good._id]: { good, count: newCount } } } else { delete state[good._id] return { ...state } } } else { return undefined } } if (type === 'CART_DEL') { delete state[good._id] return { ...state } } if (type === 'CART_SET') { if (count > 0) { return { ...state, [good._id]: { good, count } } } else { delete state[good._id] return { ...state } } } if (type === 'CART_CLEAR') { return {} } return state } // акшоны для cartReducer // Добавление товара.Должен добавлять новый ключ в state, или обновлять, если ключа в state ранее не было, увеличивая количество const actionCartAdd = (good, count = 1) => ({ type: 'CART_ADD', good, count }) // Уменьшение количества товара.Должен уменьшать количество товара в state, или удалять его если количество будет 0 или отрицательным const actionCartSub = (good, count = 1) => ({ type: 'CART_SUB', count, good }) // Удаление товара.Должен удалять ключ из state const actionCartDel = (good) => ({ type: 'CART_DEL', good }) // Задание количества товара.В отличие от добавления и уменьшения, не учитывает того количества, которое уже было в корзине, а тупо назначает количество поверху(или создает новый ключ, если в корзине товара не было).Если count 0 или отрицательное число - удаляем ключ из корзины; const actionCartSet = (good, count = 1) => ({ type: 'CART_SET', count, good }) // Очистка корзины.state должен стать пустым объектом { } const actionCartClear = () => ({ type: 'CART_CLEAR' }) // объект со всеми редьюсерами const reducers = { promise: localStoredReducer(promiseReducer, 'promise'), auth: localStoredReducer(authReducer, 'auth'), cart: localStoredReducer(cartReducer, 'cart'), } const totalReducer = combineReducers(reducers) const store = createStore(totalReducer) store.subscribe(() => console.log(store.getState())) // для контроля выводим все изменения в магазине в консоль // GraphQL запросы переписанные сразу в акшоны ======================================================================================================== // Запрос на список корневых категорий (без родителей) const actionCategoryFind = () => actionPromise('CategoryFind', gql(`query baseCategory($searchVariablesCategory: String){ CategoryFind(query: $searchVariablesCategory){ _id name parent { _id name } } }`, { searchVariablesCategory: JSON.stringify([{ parent: null }]) })) store.dispatch(actionCategoryFind()) // и сразу же все запустили, чтоб постоянно отрисовывалась // Запрос для получения одной категории с товарами и картинками const actionCategoryFindOne = _id => actionPromise('CategoryFindOne', gql(`query categoryFindOne($searchVariablesCategoryOne: String,) { CategoryFindOne(query: $searchVariablesCategoryOne){ _id name goods{ _id name description price images{ url } } subCategories{ _id name } } }`, { searchVariablesCategoryOne: JSON.stringify([{ _id }]) })) // Запрос на получение товара с описанием и картинками const actionGoodFindOne = _id => actionPromise('GoodFindOne', gql(`query oneGoodWithImages($searchVariablesGoodOne: String) { GoodFindOne(query: $searchVariablesGoodOne){ _id name price description images { url } } }`, { searchVariablesGoodOne: JSON.stringify([{ _id }]) })) // Запрос на логин const actionLogin = (login, password) => actionPromise('login', gql(`query login($login: String, $password: String) { login(login: $login, password: $password) }`, { login, password })) // показываем ошибку при авторизации, если неправильный логин или пароль function mistakeLogin() { main.innerHTML = `

Вы ввели неправильный логин или пароль

` const refresh = document.getElementById('refreshBtn') refresh.addEventListener('click', () => { location.reload() }) } // Запрос на логин и последующую логинизацию в authReduser (thunk) const actionFullLogin = (login, password) => async dispatch => { const token = await dispatch(actionLogin(login, password)) if (token != null) { if (typeof (token) === 'string') { dispatch(actionAuthLogin(token)) localStorage.authToken = token } } else { mistakeLogin() } } // Запрос на регистрацию const actionUserCreate = (login, password) => actionPromise('UserRegistrate', gql(`mutation registration($login:String,$password:String ){ UserUpsert(user:{ login:$login, password:$password }){ _id createdAt } }`, { login, password })) // показываем ошибку при регистрации, если логин занят function mistakeRegistration() { main.innerHTML = `

Этот логин занят. Повторите регистрацию с уникальным логином или авторизуйтесь

` const refresh = document.getElementById('refreshBtn') refresh.addEventListener('click', () => { location.reload() }) } // Запрос на регистрацию и сразу на авторизацию пользователя на странице (thunk) const actionFullUserCreate = (login, password) => async dispatch => { try { const registration = await dispatch(actionUserCreate(login, password)) if (registration._id !== 'null') { dispatch(actionFullLogin(login, password)) } } catch (e) { mistakeRegistration() } } // Запрос истории заказов const actionOrderFind = () => actionPromise('OrderFind', gql(`query order($order: String){ OrderFind(query: $order){ createdAt total orderGoods{ good { name price images { url } } total count } } }`, { order: JSON.stringify([{}]) })) // запрос отправку заказа на сервер const actionOrder = (goods) => actionPromise('orderCreate', gql(`mutation myOrder($createOrder: OrderInput){ OrderUpsert(order: $createOrder) { orderGoods{ count good{ _id } } } }`, { createOrder: { orderGoods: goods } })) // запрос отправку заказа на сервер с последующей очисткой корзины (thunk) const actionFillOrder = () => async dispatch => { // создаем массив с параметрами для диспатча let arrWithGoods = [] for (const [id, { count }] of Object.entries(store.getState().cart)) { arrWithGoods.push({ count: count, good: { _id: id } }) } // оформляем заказ await dispatch(actionOrder(arrWithGoods)) // и как задиспатчится заказ чистим корзину store.dispatch(actionCartClear()) } // Модуль ========================================================================================================= // отрисовка в main списка товаров из категории const drawGoods = () => { const [, route] = location.hash.split('/') if (route !== 'category') return const { status, payload, error } = store.getState().promise.CategoryFindOne if (status === 'PENDING') { main.innerHTML = `` } if (status === 'FULFILLED') { const { _id, name, goods, subCategories } = payload // подумать, нужны ли ид и субкатегория (субкатегория может ыть нужна, чтобы отрисовать ее в ) main.innerHTML = `

Категория: ${name}

` // рисуем блок для субкатегорий const drawSubCat = document.createElement('div') drawSubCat.id = 'drawSubCat' main.append(drawSubCat) // отрисовываем карточки категорий // тут проблема. в айфонах subCategories из-за этого вот так с проверкой if (subCategories != null) { for (const { _id, name } of subCategories) { drawSubCat.innerHTML += `

${name}

Подробнее
` } } // рисуем блок для товаров в категории const goodsList = document.createElement('div') goodsList.id = 'goodList' main.append(goodsList) // отрисовываем карточку товара в разделе категорий if (goods != null) { for (const { name, description, images, price, _id } of goods) { // удалить description из запроса и из отрисовки let img = url.slice(0, -7) + images[0].url goodsList.innerHTML += `

${name}

Цена: ${price} грн.

Подробнее
` } } } } store.subscribe(drawGoods) // отрисовка товара после нажатия на карточку товара const drawGoodOne = () => { const [, route] = location.hash.split('/') if (route !== 'good') return const { status, payload, error } = store.getState().promise.GoodFindOne if (status === 'PENDING') { main.innerHTML = `` } if (status === 'FULFILLED') { const { _id, name, description, price, images } = payload let img = url.slice(0, -7) + images[0].url main.innerHTML = `

${name}

Цена: ${price} грн./шт.

Описание:

${description}




` // включаем кнопку "добавить товар", только если указали количество товара addToCartInput.addEventListener('change', () => { if (addToCartInput.value == 0) { addToCartBtn.disabled = true } else { addToCartBtn.disabled = false } // addToCartBtn.disabled = (addToCartInput.value == 0 || false) // или можно написать вот так эту проверку }) // Добавляем товар в корзину по клику кнопки "добавить" addToCartBtn.addEventListener('click', () => { store.dispatch(actionCartAdd({ _id, name, description, price, img }, +addToCartInput.value)) }) } } store.subscribe(drawGoodOne) //отрисовка истории заказов пользователя const history = () => { const [, route] = location.hash.split('/') if (route !== 'history') return const { status, payload, error } = store.getState().promise.OrderFind if (status === 'PENDING') { main.innerHTML = `` } if (status === 'FULFILLED') { main.innerHTML = `

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

` for (const { createdAt, total, orderGoods } of payload) { const orderFindCart = document.createElement('div') orderFindCart.classList = 'orderFindCart' main.append(orderFindCart) // формируем дату создания поста const dateOfOrder = new Date(+createdAt) const months = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'] const dateOfOrderParse = `${dateOfOrder.getDate() < 10 ? '0' + dateOfOrder.getDate() : dateOfOrder.getDate()} ${months[dateOfOrder.getMonth()]} ${dateOfOrder.getFullYear()} ${dateOfOrder.getHours()}:${dateOfOrder.getMinutes() < 10 ? '0' + dateOfOrder.getMinutes() : dateOfOrder.getMinutes()}` orderFindCart.innerHTML = `

Создано: ${dateOfOrderParse}

Сумма заказа: ${total} тугриков

` for (const { good: { name, price, images }, count, total } of orderGoods) { let img = url.slice(0, -7) + images[0].url orderFindCart.innerHTML += `

Товар: ${name}

Цена товара: ${price}

Количество товара в заказе: ${count}

Итоговая сумма: ${total}

` } } } } store.subscribe(history) // отрисовываем левое меню const leftMenu = () => { const { status, payload, error } = store.getState().promise.CategoryFind if (status === 'FULFILLED' && payload) { aside.innerHTML = '' aside.innerHTML = `Главная` for (const { _id, name } of payload) { aside.innerHTML += `${name}` } } } store.subscribe(leftMenu) // изменение цифры количества товаров в корзине (в красном лейбле) const redLabel = () => { let goodCount = 0 for (let { count } of Object.values(store.getState().cart)) { goodCount += count } document.getElementById('cartIcon').textContent = goodCount } store.subscribe(redLabel) // подписчик для отображения залогинен или нет пользователь, а также для отображения страниц регистрации и логинизации в шапке const userStatus = () => { if (!(store.getState().auth.payload?.sub?.login)) { login.innerHTML = `Авторизоваться
Зарегистрироваться
` } else { login.innerHTML = `Привет, ${store.getState().auth.payload?.sub?.login}
История заказов
Выйти` logout.onclick = () => { store.dispatch(actionAuthLogout()) } } } store.subscribe(userStatus) // функция для страницы корзины. из-за тго, что очень массивная страница с большим функционалом, отрисовывааем ее отдельно function cartPage() { const [, route] = location.hash.split('/') if (route !== 'cart') return if (Object.keys(store.getState().cart).length !== 0) { main.innerHTML = '

Корзина

' // подсчитываем сумму всего заказа (изначально при входе в корзину) let summ = 0 for (const { count, good: { price } } of Object.values(store.getState().cart)) { summ += (count * price) } // создаем с помощью цикла карточки товаров в заказе for (const [id, { count, good: { description, img, name, price, _id } }] of Object.entries(store.getState().cart)) { const goodInCart = document.createElement('div') goodInCart.id = "goodInCart" goodInCart.innerHTML = `

${name}

` main.append(goodInCart) const goodInCartContent = document.createElement('div') goodInCartContent.id = 'goodInCartContent' goodInCart.append(goodInCartContent) const goodInCartIimg = document.createElement('div') goodInCartIimg.id = 'goodInCartIimg' goodInCartIimg.innerHTML = `` goodInCartContent.append(goodInCartIimg) const goodInCartIDescription = document.createElement('div') goodInCartIDescription.id = 'goodInCartIDescription' goodInCartContent.append(goodInCartIDescription) // рисуем кнопки на карточке товара //кнопка удаления const delBtn = document.createElement('button') delBtn.innerHTML = '-' goodInCartIDescription.append(delBtn) const goodInCartValue = document.createElement('input') goodInCartValue.id = 'goodInCartValue' goodInCartValue.value = count goodInCartIDescription.append(goodInCartValue) // кнопка добавления const addBtn = document.createElement('button') addBtn.innerHTML = '+' goodInCartIDescription.append(addBtn) // создаем блок с кнопками управления const blockWithButtons = document.createElement('div') goodInCartIDescription.append(blockWithButtons) //кнопка универсального добавления товара const cartSetBtn = document.createElement('button') cartSetBtn.innerHTML = 'Добавить товар' blockWithButtons.append(cartSetBtn) // кнопка удаления товара из корзины const cartDelBtn = document.createElement('button') cartDelBtn.innerHTML = 'Удалить товар' blockWithButtons.append(cartDelBtn) const goodInCartAbout = document.createElement('p') goodInCartAbout.innerHTML = `Описание: ${description}.

Цена за ед. ${price} тугриков.` goodInCartIDescription.append(goodInCartAbout) const goodInCartFullPrice = document.createElement('p') goodInCartFullPrice.innerHTML = `Итоговая стоимость за товар: ${count * price} тугриков` // goodInCartFullPrice.innerHTML = `Итоговая стоимость за товар: ${store.getState().cart[_id].count * price} тугриков` goodInCartIDescription.append(goodInCartFullPrice) // блок колбеков на кнопках // коллбек на кнопку + addBtn.addEventListener('click', async () => { await store.dispatch(actionCartAdd({ _id, name, description, price, img })) const newCount = store.getState().cart[_id].count goodInCartValue.value = newCount // изменяем стоимость на карточке товара goodInCartFullPrice.innerHTML = `Итоговая стоимость за товар: ${newCount * price} тугриков` }) // коллбек на кнопку - delBtn.addEventListener('click', async () => { await store.dispatch(actionCartSub({ _id, name, description, price, img })) // прверяем, если после этого товара не осталось (удалили последний товар), перерисовываем страницу. если товар еще есть в заказе, просто понимажем его счетчик if (store.getState().cart[_id]) { const newCount = store.getState().cart[_id].count goodInCartValue.value = newCount // изменяем стоимость карточки goodInCartFullPrice.innerHTML = `Итоговая стоимость за товар: ${newCount * price} тугриков` } else { cartPage() } }) // коллбек на кнопку "Добавить товар" cartSetBtn.addEventListener('click', async () => { const newCount = goodInCartValue.value // проверяем число на 0 или больше if (newCount > 0) { await store.dispatch(actionCartSet({ _id, name, description, price, img }, +newCount)) // изменяем стоимость карточки goodInCartFullPrice.innerHTML = `Итоговая стоимость за товар: ${newCount * price} тугриков` // изменяем общую сумму заказа // cartOrderFullPrice.innerHTML = `Сумма заказа: ${summ} тугриков` } else { await store.dispatch(actionCartDel({ _id })) // и сразу обновляем страницу корзины cartPage() } }) // коллбек на кнопку "Удалить товар" cartDelBtn.addEventListener('click', async () => { await store.dispatch(actionCartDel({ _id })) // и сразу обновляем страницу корзины cartPage() }) } // отрисовываем блок под карточками товаров с общей стоимостью покупки и кнопками "Оформить заказ" и "Очистить корзину" const cartOrderFullPrice = document.createElement('p') cartOrderFullPrice.innerHTML = `Общая сумма заказа: ${summ} тугриков` main.append(cartOrderFullPrice) const cartOrderCreate = document.createElement('button') cartOrderCreate.id = 'cartOrderCreateBtn' cartOrderCreate.innerHTML = 'Оформить заказ' main.append(cartOrderCreate) const breakLine = document.createElement('br') main.append(breakLine) const cartClean = document.createElement('button') cartClean.innerHTML = 'Очистить корзину' main.append(cartClean) // коллбек на кнопку очистки корзины cartClean.addEventListener('click', async () => { await store.dispatch(actionCartClear()) // и сразу обновляем страницу корзины cartPage() }) // коллбек на кнопку оформления заказа cartOrderCreate.addEventListener('click', async () => { await store.dispatch(actionFillOrder()) // и сразу обновляем страницу корзины cartPage() }) // подписчик для изменения отображения на странице корзины const summFinally = () => { // просчитываем полную сумму заказа let summ = 0 for (const { count, good: { price } } of Object.values(store.getState().cart)) { summ += (count * price) } // меняем общую стоимость заказа при изменении стора cartOrderFullPrice.innerHTML = `Сумма заказа: ${summ} тугриков` } store.subscribe(summFinally) } else { main.innerHTML = `

Ваша корзина пуста.Чтобы сделать заказ, сначала добавьте товары.

` } } // функция - конструктор формы логин/пароль ========================================================================================================= function LoginForm(parent) { function Password(parent, open) { // отображение формы для пароля const inputPassword = document.createElement('input') inputPassword.type = 'password' inputPassword.placeholder = 'Insert password' parent.append(inputPassword) // создание и отображение чекбокса (открыть/скрыть пароль) const inputCheckbox = document.createElement('input') inputCheckbox.type = 'checkbox' inputCheckbox.checked = false parent.append(inputCheckbox) // создаем геттеры this.getValue = () => inputPassword.value this.getOpen = () => inputCheckbox.checked // создаем сеттеры this.setValue = (value) => inputPassword.value = value this.setOpen = (open) => { if (open === true) { inputPassword.type = 'text' inputCheckbox.checked = true } if (open === false) { inputPassword.type = 'password' inputCheckbox.checked = false } return inputPassword.type, inputCheckbox.checked } // starting onChange inputPassword.addEventListener('input', () => { this.onChange(inputPassword.value) }) // starting onOpenChange + change inputPasswor hide/see inputCheckbox.addEventListener('change', () => { this.setOpen(inputCheckbox.checked) this.onOpenChange(inputCheckbox.checked) }) } function Login(parent) { // создаем и отрысовываем поле для ввода логина const inputLogin = document.createElement('input') inputLogin.type = 'text' inputLogin.placeholder = 'Insert login' parent.append(inputLogin) // создаем геттеры this.getValue = () => inputLogin.value // создаем сеттеры this.setValue = (value) => inputLogin.value = value // starting onChange inputLogin.addEventListener('input', () => { this.onChange(inputLogin.value) }) } // создание и отрисовывание формы для логина/пароля // const form = document.createElement('form') // если формируем форму, а не обычный див, тогда не работает запрос и появляется в адресной строке "?" после домена в урле const form = document.createElement('div') parent.append(form) const loginLabel = document.createElement('label') loginLabel.innerText = 'Login:' form.append(loginLabel) let breakSymbol = document.createElement('br') form.append(breakSymbol) // отрисовываем поле логина const login = new Login(form) breakSymbol = document.createElement('br') form.append(breakSymbol) const passwordLabel = document.createElement('label') passwordLabel.innerText = 'Password:' form.append(passwordLabel) breakSymbol = document.createElement('br') form.append(breakSymbol) // отрисовываем поле для пароля const password = new Password(form, true) breakSymbol = document.createElement('br') form.append(breakSymbol) // создание и отрисовывание кнопки для входа const confirmBtn = document.createElement('button') confirmBtn.innerText = 'Confirm' confirmBtn.id = 'confirmBtn' confirmBtn.type = 'submit' confirmBtn.style.marginTop = '10px' confirmBtn.disabled = true form.append(confirmBtn) // change confirmBtn.disabled function checkButton() { if (login.getValue() !== '' && password.getValue() !== '') { confirmBtn.disabled = false } else { confirmBtn.disabled = true } return confirmBtn.disabled } checkButton() // listening password and login login.onChange = password.onChange = checkButton // create getters this.getPasswordValue = () => password.getValue() this.getPasswordOpen = () => password.getOpen() this.getLoginValue = () => login.getValue() this.getButtonStatus = () => confirmBtn.disabled // create setters this.setPasswordValue = (value) => password.setValue(value) this.setLoginValue = (value) => login.setValue(value) this.setPasswordOpen = (status) => password.setOpen(status) // create callbacks this.onOpenChange = open => password.onOpenChange = open } // проверяем, что сейчас находится в урле и, исходя из приставки в урле, запускаем нужную функцию window.onhashchange = () => { const [, route, _id] = location.hash.split('/') const routes = { category() { store.dispatch(actionCategoryFindOne(_id)) }, good() { store.dispatch(actionGoodFindOne(_id)) }, login() { if (!localStorage.authToken) { // если пользователь залогинен, то ему больше не рисовать форму логина main.innerHTML = '

Авторизуйтесь

' //нарисовать форму логина, которая по нажатию кнопки Login делает store.dispatch(actionFullLogin(login, password)) const logForm = new LoginForm(main) confirmBtn.addEventListener('click', async () => { localStorage.removeItem('authToken') // на всякий случай (а нужно ли?) удаляю старый authToken перед регистрацией await store.dispatch(actionFullLogin(logForm.getLoginValue(), logForm.getPasswordValue())) // if (store.getState().promise.login.payload != null) { if ((Object.keys(store.getState().auth)).length) { main.innerHTML = `

С возвращением, ${logForm.getLoginValue()}!

` } }) } }, register() { if (!localStorage.authToken) { // если пользователь залогинен, то ему больше не отображать страницу регистрации (не рисовать форму) main.innerHTML = `

Зарегистрируйтесь

` // Запрос на регистрацию. Страница # / register /.В onhashchange НЕ происходит dispatch, вместо этого рисуется форма логина из предыдущих ДЗ, по логину же происходит dispatch const regForm = new LoginForm(main) confirmBtn.addEventListener('click', async () => { localStorage.removeItem('authToken') // на всякий случай (а нужно ли?) удаляю старый authToken перед регистрацией await store.dispatch(actionFullUserCreate(regForm.getLoginValue(), regForm.getPasswordValue())) if ((Object.keys(store.getState().auth)).length) { main.innerHTML = `

Поздравляем, регистраций прошла успешно!

` } }) } }, history() { store.dispatch(actionOrderFind()) }, cart() { if (localStorage.authToken) { // если пользователь авторизован - показываем корзину, если нет - предлагаем авторизоваться! cartPage() } else { main.innerHTML = `

Корзина

Для просмотра корзины Вам необходимо авторизоваться!

Авторизация` } } } if (route in routes) { routes[route]() } } window.onhashchange() // 3. разобраться с тем, почему корзина хуйней страдает // 6. сonst form = document.createElement('form') // если формируем форму, а не обычный див на логине, тогда не работает запрос и появляется в адресной строке "?" после домена в урле