const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms)) 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 /*state = { ...state, ...newState }*/ //если смог, то обновляем state for (let cb of cbs) cb() //и запускаем подписчиков } } return { getState, //добавление функции getState в результирующий объект dispatch, subscribe //добавление subscribe в объект } } function promiseReducer(state = {}, { promiseName, type, status, payload, error }) { if (type === 'PROMISE') { return { ...state, [promiseName]: { status, payload, error } } } return state } const actionPending = promiseName => ({ promiseName, type: 'PROMISE', status: 'PENDING' }) const actionFulfilled = (promiseName, payload) => ({ promiseName, type: 'PROMISE', status: 'FULFILLED', payload }) const actionRejected = (promiseName, error) => ({ promiseName, type: 'PROMISE', status: 'REJECTED', error }) const actionPromise = (promiseName, promise) => async dispatch => { dispatch(actionPending(promiseName)) //сигнализируем redux, что промис начался try { const payload = await promise //ожидаем промиса dispatch(actionFulfilled(promiseName, payload)) //сигнализируем redux, что промис успешно выполнен return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса } catch (error) { dispatch(actionRejected(promiseName, error)) //в случае ошибки - сигнализируем redux, что промис несложился } } // const store = createStore(promiseReducer) // store.subscribe(() => console.log(store.getState())) // store.dispatch(actionPromise('delay', delay(1000))) // store.dispatch(actionPromise('luke', fetch("https://swapi.dev/api/people/1").then(res => res.json()))) // store.dispatch(actionPromise('tatooine', fetch("https://swapi.dev/api/planets/1").then(res => res.json()))) //по итогу должен получится какой-то такой state: /* { delay: {status: 'FULFILLED', payload: 1000, error: undefined}, luke: {status: 'FULFILLED', payload: { ..... траливали про люка}, error: undefined}, tatooine: {status: 'FULFILLED', payload: { ..... траливали про планету татуин}, error: undefined}, } */ /* authReducer Этот редьюсер предназначен для хранения и обработки состояния залогиненности пользователя. У него бывает два вида состояний: Залогинен. state вида: {token: "jwt токен", payload: {.....раскодированная инфа из токена}} Незалогинен. state вида: {} (пустой объект) Редьюсер обрабатывает два типа экшонов: AUTH_LOGIN. Логинимся. Редьюсер должен попытаться раскодировать токен, и если это получилось, вернуть новый стейт вида {token, payload} Если токен не удалось раскодировать, вернуть пустой объект (т. е. разлогиниться) AUTH_LOGOUT. Разлогиниваемся. Возвращаем пустой объект. Проверочный код (и экшенкриейторы бонусом):*/ function jwtDecode(token) { let result; try { let secondPartToken = token.split('.')[1]; result = JSON.parse(atob(secondPartToken)); } catch (e) { } return result; } function authReducer(state = {}, { type, token }) { if (type === 'AUTH_LOGIN') { payload = jwtDecode(token) console.log(payload) if (payload) return { token, payload } } if (type === 'AUTH_LOGOUT') return {} return state } const actionAuthLogin = token => ({ type: 'AUTH_LOGIN', token }) const actionAuthLogout = () => ({ type: 'AUTH_LOGOUT' }) // const store = createStore(authReducer) // store.subscribe(() => console.log(store.getState())) // store.dispatch(actionAuthLogout()) // store.dispatch(actionAuthLogin(token)) /*{ token: "eyJhbGc.....", payload: { "sub": { "id": "6377e133b74e1f5f2ec1c125", "login": "test5", "acl": [ "6377e133b74e1f5f2ec1c125", "user" ] }, "iat": 1668812458 } }*/ //store.dispatch(actionAuthLogout()) // {} /*cartReducer Редьюсер, который хранит состояние корзины во время серфинга по интернет-магазину. Похож на редьюсер ларька, только работает немного наоборот - в то время как в ларьке товар уменьшается, тут он добавляется. Состояние: { idТовара1: {count: количество1, good: {....инфа про товар с бэкэнда, включая цену, описание и картинки}}, idТовара2: {count: количество2, good: {....инфа про товар с бэкэнда, включая цену, описание и картинки}}, } Типы экшенов */ // Добавление товара. Должен добавлять новый ключ в state, или обновлять, если ключа в state ранее не было, увеличивая количество const actionCartAdd = (good, count = 1) => ({ type: 'CART_ADD', count, good }) // Уменьшение количества товара. Должен уменьшать количество товара в 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' }) /* Проверочный код Наличие good как ключа в state немного избыточно - для оформления заказа достаточно id и количества. Однако это позволит хранить в редьюсере всю нужную информацию, например, для отображения страницы корзины без лишних запросов на сервер. На странице корзины обычно есть и описание, и цена, и фоточки. Проверочный код ниже использует в качестве id более наглядные вещи. */ function cartReducer(state = {}, { type, count, good }) { if (type === 'CART_ADD') { if (good._id in state) { return { ...state, [good._id]: { good, count: state[good._id].count + count } } } else { return { ...state, [good._id]: { good, count } } } } if (type === 'CART_SUB') { let result = {} if (good._id in state) { if ((state[good._id].count - count) <= 0) { newState = { ...state } delete newState[good._id] return newState } else { return { ...state, [good._id]: { good, count: state[good._id].count - count } } } } } if (type === 'CART_DEL') { let newState = { ...state } delete newState[good._id] return newState } // Задание количества товара. В отличие от добавления и уменьшения, не учитывает того количества, которое уже было в корзине, а тупо назначает количество поверху (или создает новый ключ, если в корзине товара не было). Если count 0 или отрицательное число - удаляем ключ из корзины; if (type === 'CART_SET') { if (good._id in state && count <= 0) { let newState = { ...state } delete newState[good._id] return newState } else { let newState = { ...state, [good._id]: { good, count } } return newState } } if (type === 'CART_CLEAR') return {} return state } 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 } const reducers = { promise: promiseReducer, //допилить много имен для многих промисо auth: authReducer, //часть предыдущего ДЗ cart: cartReducer, //часть предыдущего ДЗ } const totalReducer = combineReducers(reducers) function localStoredReducer(originalReducer, localStorageKey) { let counter = 0 function wrapper(state, action) { if (counter === 0) { counter++ if (localStorage[localStorageKey]) { let result = JSON.parse(localStorage[localStorageKey]) if (result) return result } } else { let result = originalReducer(state, action) localStorage[localStorageKey] = JSON.stringify(result) return result } } return wrapper } const store = createStore(localStoredReducer(totalReducer, "state")) //не забудьте combineReducers если он у вас уже есть store.subscribe(() => console.log(store.getState())) const actionRootCats = () => actionPromise("RootCats", gqlRootCats()) store.dispatch(actionRootCats()) store.subscribe(() => { let result = store.getState().promise.RootCats.payload if (result) { aside.innerHTML = "" for (let { _id, name } of result) { aside.innerHTML += `${name}` } } }) const actionCategoryGoodsAndSubCategoryGoods = (_id) => actionPromise("CategoryGoodsAndSubCategoryGoods", gqlCategoryGoodsAndSubCategoryGoods(_id)) store.dispatch(actionCategoryGoodsAndSubCategoryGoods('62c94b10b74e1f5f2ec1a0dd')) store.subscribe(() => { if (location.hash === '') { const { status, payload, error } = store.getState().promise.CategoryGoodsAndSubCategoryGoods if (status === 'PENDING') { main.innerHTML = `` } if (status === 'FULFILLED') { if (payload) categotyToMain(payload) } if (status === 'REJECTED') { main.innerHTML = `Что-то пошло не так, но мы скоро всё починим ;( \n сообщить о проблеме: blablatest123@gmail.com` } } }) function categotyToMain(result) { main.innerHTML = "" let breadcrumbsContainer = document.createElement('div') main.append(breadcrumbsContainer) breadcrumbsContainer.classList.add('breadcrumbsContainer') let breadcrumbsHome = document.createElement('a') breadcrumbsContainer.append(breadcrumbsHome) breadcrumbsHome.innerText = 'Mystore.com' breadcrumbsHome.href = location.origin + location.pathname if (result.parent){ breadcrumbsContainer.innerHTML += `
>
...
>
${result.parent.name}` } let h1 = document.createElement('h1') h1.innerText = result.name main.append(h1) if (result.subCategories && result.subCategories.length>0){ const containerSubcategoryLink = document.createElement('div') containerSubcategoryLink.classList.add('containerSubcategoryLink') main.append(containerSubcategoryLink) for(const {_id, name} of result.subCategories){ console.log(name) let subcategoryLink = document.createElement('a') containerSubcategoryLink.append(subcategoryLink) subcategoryLink.href = `#/category/${_id}` subcategoryLink.classList.add('subcategoryLink') subcategoryLink.innerText = name } } if (!result.goods){ let emptyCategoryMessage = document.createElement('div') main.append(emptyCategoryMessage) emptyCategoryMessage.innerText = 'В этой категории пока нет товаров :(' return } for (let { _id, name, images, price} of result?.goods) { let card = document.createElement('a') card.id = _id card.href = `#/good/${_id}` card.classList.add('card') main.append(card) let cardImgWrapper = document.createElement('div') cardImgWrapper.classList.add('cardImgWrapper') card.append(cardImgWrapper) let cardImg = document.createElement('img') cardImg.classList.add('cardImg') cardImgWrapper.append(cardImg) if (images.length>0){ let { url } = images[0] cardImg.src = hostname + url } else{ cardImg.src = 'https://lukachi.com.ua/source/default-image.jpg' } let cardName = document.createElement('h2') cardName.classList.add('cardName') cardName.innerText = name card.append(cardName) let cardPrice = document.createElement('div') cardPrice.classList.add('cardPrice') cardPrice.innerText = price + " грн" card.append(cardPrice) } } let hostname = 'http://shop-roles.node.ed.asmer.org.ua/' // store.subscribe(() => { // let result = store.getState().promise.CategoryGoodsAndSubCategoryGoods?.payload // const [, route, _id] = location.hash.split('/') // if (result && route === "category") { // categotyToMain(result) // } // }) store.subscribe(() => { const [, route, _id] = location.hash.split('/') if (route !== "category") return const { status, payload, error } = store.getState().promise.CategoryGoodsAndSubCategoryGoods if (status === 'PENDING') { main.innerHTML = `` } if (status === 'FULFILLED' && payload) { categotyToMain(payload) } if (status === 'REJECTED') { main.innerHTML = `Что-то пошло не так, но мы скоро всё починим ;( \n сообщить о проблеме: blablatest123@gmail.com` } }) // store.subscribe(() => { // let result = store.getState().promise.OneGoodWithDescriptionAndImages?.payload // const [, route, _id] = location.hash.split('/') // if (result && route === "good") { // main.innerHTML = "" // let h1 = document.createElement('h1') // h1.innerText = result.name // main.append(h1) // let imgSlider = document.createElement('div') // imgSlider.classList.add('imgSlider') // main.append(imgSlider) // for (let { url } of result.images) { // imgSlider.innerHTML += `` // } // let asideDesriptionPriceCard = document.createElement('div') // asideDesriptionPriceCard.classList.add('asideDesriptionPriceCard') // main.append(asideDesriptionPriceCard) // let goodPrice = document.createElement('div') // goodPrice.classList.add('goodPrice') // goodPrice.innerText = result.price + ' грн' // asideDesriptionPriceCard.append(goodPrice) // let goodCountInput = document.createElement('input') // goodCountInput.classList.add('goodCountInput') // goodCountInput.type = 'number' // goodCountInput.min = 1 // goodCountInput.value = 1 // asideDesriptionPriceCard.append(goodCountInput) // let goodButtonAddToCart = document.createElement('a') // goodButtonAddToCart.classList.add('goodButtonAddToCart') // goodButtonAddToCart.innerText = 'В КОРЗИНУ' // goodButtonAddToCart.onclick = () => store.dispatch(actionCartAdd(result, +goodCountInput.value)) // asideDesriptionPriceCard.append(goodButtonAddToCart) // let description = document.createElement('div') // description.classList.add('description') // description.innerText = result.description // asideDesriptionPriceCard.append(description) // } // }) store.subscribe(() => { const [, route, _id] = location.hash.split('/') if (route !== "good") return const { status, payload, error } = store.getState().promise.OneGoodWithDescriptionAndImages if (status === 'PENDING') { main.innerHTML = `` } if (status === 'FULFILLED' && payload) { main.innerHTML = "" let h1 = document.createElement('h1') h1.innerText = payload.name main.append(h1) let imgSlider = document.createElement('div') imgSlider.classList.add('imgSlider') main.append(imgSlider) for (let { url } of payload.images) { imgSlider.innerHTML += `` } let asideDesriptionPriceCard = document.createElement('div') asideDesriptionPriceCard.classList.add('asideDesriptionPriceCard') main.append(asideDesriptionPriceCard) let goodPrice = document.createElement('div') goodPrice.classList.add('goodPrice') goodPrice.innerText = payload.price + ' грн' asideDesriptionPriceCard.append(goodPrice) let goodCountInput = document.createElement('input') goodCountInput.classList.add('goodCountInput') goodCountInput.type = 'number' goodCountInput.min = 1 goodCountInput.value = 1 asideDesriptionPriceCard.append(goodCountInput) let goodButtonAddToCart = document.createElement('a') goodButtonAddToCart.classList.add('goodButtonAddToCart') goodButtonAddToCart.innerText = 'В КОРЗИНУ' goodButtonAddToCart.onclick = () => store.dispatch(actionCartAdd(payload, +goodCountInput.value)) asideDesriptionPriceCard.append(goodButtonAddToCart) let description = document.createElement('div') description.classList.add('description') description.innerText = payload.description asideDesriptionPriceCard.append(description) } if (status === 'REJECTED') { main.innerHTML = `Что-то пошло не так, но мы скоро всё починим ;( \n сообщить о проблеме: blablatest123@gmail.com` } }) const actionOneGoodWithDescriptionAndImages = (_id) => actionPromise("OneGoodWithDescriptionAndImages", gqlOneGoodWithDescriptionAndImages(_id)) store.subscribe(() => { let result = 0 let arrResult = Object.values(store.getState().cart) for (let { count } of arrResult) { result += count } cartIcon.innerText = result }) store.subscribe(() => { let arrResult = Object.values(store.getState().cart) const [, route, _id] = location.hash.split('/') if (arrResult && route === "cart") { cartPageAdd() } }) window.onhashchange = () => { const [, route, _id] = location.hash.split('/') const routes = { category() { store.dispatch(actionCategoryGoodsAndSubCategoryGoods(_id)) }, good() { store.dispatch(actionOneGoodWithDescriptionAndImages(_id)) // тут был store.dispatch goodById console.log('good', _id) }, // login() { // console.log('А ТУТ ЩА ДОЛЖНА БЫТЬ ФОРМА ЛОГИНА') // //нарисовать форму логина, которая по нажатию кнопки Login делает store.dispatch(actionFullLogin(login, password)) // }, // register() { // //нарисовать форму регистрации, которая по нажатию кнопки Login делает store.dispatch(actionFullRegister(login, password)) // }, cart() { cartPageAdd() }, orders() { store.dispatch(actionOrderFind()) } } if (route in routes) { routes[route]() } } function cartPageAdd() { let arrResult = Object.values(store.getState().cart) const [, route, _id] = location.hash.split('/') if (arrResult && route === "cart") { main.innerHTML = "" let h1 = document.createElement('h1') h1.innerText = "Оформление заказа" main.append(h1) let orderTable = document.createElement('table') main.append(orderTable) orderTable.classList.add('table_price') let orderTableBody = document.createElement('tbody') orderTable.append(orderTableBody) let firstTr = document.createElement('tr') orderTableBody.append(firstTr) firstTr.innerHTML = `Название товараЦена шт.КоличествоИтого` let counter = 1 let summ = 0 for (let { good, count } of arrResult) { console.log(count) let trGood = document.createElement('tr') orderTableBody.append(trGood) trGood.innerHTML = ` ${counter} ${good.name} ${good.price}` let tdInput = document.createElement('td') trGood.append(tdInput) let inputCount = document.createElement('input') inputCount.type = 'number' inputCount.min = 1 inputCount.value = count inputCount.oninput = () => store.dispatch(actionCartSet(good, +inputCount.value)) tdInput.append(inputCount) let tdSummGood = document.createElement('td') trGood.append(tdSummGood) tdSummGood.innerText = count * good.price let tdDelete = document.createElement('td') trGood.append(tdDelete) let deleteButton = document.createElement('button') tdDelete.append(deleteButton) deleteButton.classList.add('deleteButton') deleteButton.innerText = 'удалить' deleteButton.onclick = () => store.dispatch(actionCartDel(good)) //НЕ РАБОТАЕТ!? trGood.innerHTML += `${count * good.price}` counter += 1 summ += count * good.price } let trSummTotal = document.createElement('tr') orderTableBody.append(trSummTotal) let tdColspan = document.createElement('td') trSummTotal.append(tdColspan) tdColspan.colSpan = 4 let tdSummTotal = document.createElement('td') trSummTotal.append(tdSummTotal) tdSummTotal.innerText = summ +'грн' tdSummTotal.classList.add('tdSumm') let tdDeleteTotal = document.createElement('td') trSummTotal.append(tdDeleteTotal) let DeleteTotalButton = document.createElement('button') tdDeleteTotal.append(DeleteTotalButton) DeleteTotalButton.innerText = 'ОЧИСТИТЬ КОРЗИНУ' DeleteTotalButton.onclick = () => store.dispatch(actionCartClear()) //orderTableBody.innerHTML += `${summ} грн` let addOrder = document.createElement('div') addOrder.classList.add('addOrder') addOrder.innerText = "ОФОРМИТЬ ЗАКАЗ" main.append(addOrder) let arrGoods = Object.values(store.getState().cart).map(({ count, good: { _id } }) => { return { count, good: { _id } } }) addOrder.onclick = () => { store.dispatch(actionFullAddOrder(arrGoods)) } } } const actionAddOrder = (arrGoods) => { return actionPromise("orderUpsert", OrderUpsert2(arrGoods)) } const actionFullAddOrder = (arrGoods) => async dispatch => { await dispatch(actionAddOrder(arrGoods)) dispatch(actionCartClear()) location.hash = '#/orders' } const actionLogin = (login, password) => { return actionPromise("logAndPass", gqllogin(login, password)) } const actionRegistration = (login, password) => { return actionPromise("newUser", gqlUserAdd(login, password)) } const actionFullLogin = (login, password) => async dispatch => { //dispatch возвращает то, что вернул thunk, возвращаемый actionLogin, а там промис, //так как actionPromise возвращает асинхронную функцию const token = await dispatch(actionLogin(login, password)) //проверьте что token - строка и отдайте его в actionAuthLogin if (typeof token === 'string') store.dispatch(actionAuthLogin(token)) } const actionFullRegistration = (login, password) => async dispatch => { const userAddPromis = await dispatch(actionRegistration(login, password)) if (userAddPromis) store.dispatch(actionFullLogin(login, password)) //dispatch возвращает то, что вернул thunk, возвращаемый actionLogin, а там промис, //так как actionPromise возвращает асинхронную функцию //const token = await dispatch(actionLogin(login, password)) //проверьте что token - строка и отдайте его в actionAuthLogin //if (typeof token === 'string') // store.dispatch(actionAuthLogin(token)) } const actionOrderFind = () => actionPromise("OrderFind", gqlOrderFind()) function LoginFormFunc(parent, passOpenDefault = open) { function Password(parent, open) { const inputPass = document.createElement('input') parent.append(inputPass) const checkboxPass = document.createElement('input') checkboxPass.type = 'checkbox' parent.append(checkboxPass) this.setValue = (value) => { inputPass.value = value if (typeof this.onChange === 'function') this.onChange(this.getValue()) // запускается по событию oninput в поле ввода, передает текст в колбэк } //задает значение this.getValue = () => inputPass.value //читает значение this.setOpen = (open) => { if (open) { checkboxPass.checked = true inputPass.type = "text" } if (!open) { checkboxPass.checked = false inputPass.type = "password" } if (typeof this.onOpenChange === 'function') this.onOpenChange(this.getOpen()) // запускается по изменению состояния открытости пароля } //задает открытость текста в поле ввода this.getOpen = () => checkboxPass.checked //читает открытость текста в поле ввода this.setOpen(open) inputPass.oninput = () => this.setValue(this.getValue()) checkboxPass.oninput = () => this.setOpen(this.getOpen()) } const LoginForm = document.createElement('div') parent.append(LoginForm) const inputLogin = document.createElement('input') LoginForm.append(inputLogin) let p = new Password(LoginForm, passOpenDefault) const inputButton = document.createElement('input') inputButton.type = 'button' inputButton.value = 'войти' inputButton.id = 'signIn' LoginForm.append(inputButton) inputButton.disabled = true const newUserAddButton = document.createElement('input') newUserAddButton.type = 'button' newUserAddButton.value = 'зарегистрироваться' newUserAddButton.id = 'register' LoginForm.append(newUserAddButton) p.onChange = () => checkButtonDisabled() inputLogin.oninput = () => checkButtonDisabled() function checkButtonDisabled() { if (p.getValue() && inputLogin.value) inputButton.disabled = false else inputButton.disabled = true } this.getLogin = () => inputLogin.value this.setLogin = (value) => { inputLogin.value = value checkButtonDisabled() } this.getPass = () => p.getValue() this.setPass = (value) => { p.setValue(value) checkButtonDisabled() } inputButton.onclick = () => { if (typeof this.inputButtonOnclick === 'function') this.inputButtonOnclick() //функция при нажатии на кнопку войти } newUserAddButton.onclick = () => { if (typeof this.newUserAddButtonOnclick === 'function') this.newUserAddButtonOnclick() } } // let LoginFormInHeader = new LoginFormFunc(user) // LoginFormInHeader.inputButtonOnclick = function () { // store.dispatch(actionFullLogin(this.getLogin(), this.getPass())) // } //vasya321986320915sf5654755ssddgfg пороль store.subscribe(() => { let result = store.getState().auth?.payload?.sub?.login if (result) { user.innerHTML = result const buttonExitLogin = document.createElement('button') buttonExitLogin.innerHTML = 'выйти' buttonExitLogin.classList.add('buttonExitLogin') buttonExitLogin.onclick = () => { store.dispatch(actionAuthLogout()) } user.append(buttonExitLogin) localStorage.authToken = store.getState().auth.token const buttonOrders = document.createElement('button') buttonOrders.innerHTML = 'история заказов' buttonOrders.classList.add('buttonOrders') buttonOrders.onclick = () => location.hash = '#/orders' user.append(buttonOrders) } else { delete localStorage.authToken user.innerHTML = "Unknown User" let LoginFormInHeader = new LoginFormFunc(user) LoginFormInHeader.inputButtonOnclick = () => { store.dispatch(actionFullLogin(this.getLogin(), this.getPass())) } LoginFormInHeader.newUserAddButtonOnclick = function () { store.dispatch(actionFullRegistration(this.getLogin(), this.getPass())) } } }) // store.subscribe(() => { // let arrResult = store.getState().promise.OrderFind?.payload // const [, route, _id] = location.hash.split('/') // if (arrResult && route === "orders") { // main.innerHTML = "" // let h1 = document.createElement('h1') // h1.innerText = "История заказов" // main.append(h1) // let orderTable = document.createElement('table') // main.append(orderTable) // orderTable.classList.add('table_price') // let firstTr = document.createElement('tr') // orderTable.append(firstTr) // firstTr.innerHTML = `Номер заказаДатаСумма, грн` // let counter = 1 // let summ = 0 // for (let { _id, createdAt, total } of arrResult) { // let createdAtFormat = new Date(+createdAt) // let year = createdAtFormat.getFullYear() // let month = createdAtFormat.getMonth() < 9 ? "0" + (createdAtFormat.getMonth() + 1) : createdAtFormat.getMonth() + 1 // let day = createdAtFormat.getDate() < 9 ? "0" + (createdAtFormat.getDate() + 1) : createdAtFormat.getDate() + 1 // let hours = createdAtFormat.getHours() < 10 ? "0" + (createdAtFormat.getHours()) : createdAtFormat.getHours() // let minutes = createdAtFormat.getMinutes() < 10 ? "0" + (createdAtFormat.getMinutes()) : createdAtFormat.getMinutes() // const createdAtForTable = `${year}.${month}.${day} ${hours}:${minutes} ` // orderTable.innerHTML += ` // ${counter} // ${_id} // ${createdAtForTable} // ${total} // ` // counter += 1 // summ += total // } // orderTable.innerHTML += `${summ} грн` // } // }) function ordersToHTML (payload) { main.innerHTML = "" let h1 = document.createElement('h1') h1.innerText = "История заказов" main.append(h1) let orderTable = document.createElement('table') main.append(orderTable) orderTable.classList.add('table_price') let orderTableBody = document.createElement('tbody') orderTable.append(orderTableBody) let firstTr = document.createElement('tr') orderTableBody.append(firstTr) firstTr.innerHTML = `Номер заказаДатаСумма, грн` let counter = 1 let summ = 0 let arrRevers = [...payload] for (let { _id, createdAt, total } of arrRevers.reverse()) { let createdAtFormat = new Date(+createdAt) let year = createdAtFormat.getFullYear() let month = createdAtFormat.getMonth() < 9 ? "0" + (createdAtFormat.getMonth() + 1) : createdAtFormat.getMonth() + 1 let day = createdAtFormat.getDate() < 10 ? "0" + (createdAtFormat.getDate()) : createdAtFormat.getDate() let hours = createdAtFormat.getHours() < 10 ? "0" + (createdAtFormat.getHours()) : createdAtFormat.getHours() let minutes = createdAtFormat.getMinutes() < 10 ? "0" + (createdAtFormat.getMinutes()) : createdAtFormat.getMinutes() const createdAtForTable = `${year}.${month}.${day} ${hours}:${minutes} ` orderTableBody.innerHTML += ` ${counter} ${_id} ${createdAtForTable} ${total} ` counter += 1 summ += total } orderTableBody.innerHTML += `${summ} грн` } store.subscribe(() => { const [, route, _id] = location.hash.split('/') if (route !== "orders") return const {status, payload, error} = store.getState().promise.OrderFind if (status === 'PENDING') { main.innerHTML = `` } if (status === 'FULFILLED' && payload) { ordersToHTML (payload) } if (status === 'REJECTED') { main.innerHTML = `Что-то пошло не так, но мы скоро всё починим ;( \n сообщить о проблеме: blablatest123@gmail.com` } })