123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829 |
- <head>
- <Header>MODULE MARKET</Header>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
- integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous" />
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"
- integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4"
- crossorigin="anonymous"></script>
- <link rel="stylesheet" href="https://cdn.reflowhq.com/v2/toolkit.min.css">
- <link rel="stylesheet" href="index.css">
- <style>
- .alert-fixed {
- position: fixed;
- top: 0px;
- left: 0px;
- width: 100%;
- z-index: 9999;
- border-radius: 0px;
- }
- </style>
- <div class="alert-fixed" id="alertsZone"></div>
- </head>
- <body>
- <div class="container-fluid">
- <nav class="navbar navbar-expand-md">
- <a class="navbar-brand" href="?#"><img src="./Img/Logo.png" width="140px"></a>
- <div class="collapse navbar-collapse" id="main-navigation">
- <ul class="navbar-nav align-items-center h3">
- <li id="loginLink" class="nav-item d-none">
- <a class="nav-link" href="?#/login/">Login</a>
- </li>
- <li id="regLink" class="nav-item d-none">
- <a class="nav-link" href="?#/register/">Register</a>
- </li>
- <li id="logoutLink" class="nav-item d-none">
- <a class="nav-link" href="?#/logout/">Logout</a>
- </li>
- <li id="cartLink" class="nav-item">
- <a class="nav-link" href="?#/cart/">
- <i class="fa badge fa-lg" id="cartCountBadge" value=0>
- <img src="./Img/корзина.png" width="60px">
- <!--<span class="badge badge-light" id="cartCountBadge">0</span>-->
- </i>
- </a>
- </li>
- <li id="historyLink" class="nav-item d-none">
- <a class="nav-link" href="#/orders/">History</a>
- </li>
- </ul>
- </div>
- </nav>
- </div>
- <div class="container-fluid">
- <div class="row">
- <div class="col-2 left">
- <div style="background-color: wheat;" class="d-flex flex-column flex-shrink-0 p-3 text-dark">
- <ul id="categoriesList" class="nav nav-pills flex-column mb-auto">
- </ul>
- </div>
- </div>
- <div class="col-8">
- <div id="main">
- </div>
- </div>
- </div>
- </div>
- <!--<header>
- <div>
- <a href="#/login/">Login</a>
- </div>
- <div>
- <a href="#/logout/">Logout</a>
- </div>
- <div>
- <a href="#/orders/">Orders History</a>
- </div>
- <div id='cartIcon'></div>
- <div>
- <a href="#/cart">Cart</a>
- </div>
- </header>
- <div id='mainContainer'>
- <aside id='aside'>
- </aside>
- <main id='main'>
- </main>
- </div>-->
- <script type="text/javascript" src="./login.js"></script>
- <script type="text/javascript" src="./alerts.js"></script>
- <script type="text/javascript" src="./pagination.js"></script>
- <script>
- const addErrorAlert = (error) => {
- addAlert(alertsZone, error, 'warning');
- }
- function jwtDecode(token) { // расщифровки токена авторизации
- if (!token || typeof token != "string")
- return undefined;
- let tokenArr = token.split(".");
- if (tokenArr.length != 3)
- return undefined;
- try {
- let tokenJsonStr = atob(tokenArr[1]);
- let tokenJson = JSON.parse(tokenJsonStr);
- return tokenJson;
- }
- catch (error) {
- addErrorAlert(error.message);
- return undefined;
- }
- }
- function combineReducers(reducers) {
- function totalReducer(totalState = {}, action) {
- const newTotalState = {} //объект, который будет хранить только новые состояния дочерних редьюсеров
- //цикл + квадратные скобочки позволяют написать код, который будет работать с любыми количеством дочерных редьюсеров
- for (const [reducerName, childReducer] of Object.entries(reducers)) {
- const newState = childReducer(totalState[reducerName], action) //запуск дочернего редьюсера
- if (newState !== totalState[reducerName]) { //если он отреагировал на action
- newTotalState[reducerName] = newState //добавляем его в newTotalState
- }
- }
- //Универсальная проверка на то, что хотя бы один дочерний редьюсер создал новый стейт:
- if (Object.values(newTotalState).length) {
- return { ...totalState, ...newTotalState } //создаем новый общий стейт, накладывая новый стейты дочерних редьюсеров на старые
- }
- return totalState //если экшен не был понят ни одним из дочерних редьюсеров, возвращаем общий стейт как был.
- }
- return totalReducer
- }
- function cartReducer(state = {}, action) { // диспетчер обработки
- switch (action.type) {
- case 'CART_ADD':
- if (action.count >= 0) {
- let newState = { ...state };
- let { count } = state[action.good._id] ?? { count: 0 };
- newState[action.good._id] = { count: action.count + count, good: { ...action.good } }
- return newState;
- }
- case 'CART_SUB':
- if (action.count >= 0) {
- let newState = { ...state };
- let { count } = state[action.good._id] ?? { count: 0 };
- if (count >= action.count) {
- newState[action.good._id] = { count: action.count - count, good: { ...action.good } }
- return newState;
- }
- }
- break;
- case 'CART_DEL':
- {
- let newState = { ...state };
- delete newState[action.good._id];
- return newState;
- }
- case 'CART_SET':
- {
- let newState = { ...state };
- newState[action.good._id] = { count: action.count, good: { ...action.good } };
- return newState;
- }
- case 'CART_SHOW':
- {
- return newState = { ...state };
- }
- case 'CART_CLEAR':
- return {};
- }
- return state;
- }
- function localStoredReducer(originalReducer, localStorageKey) {
- function wrapper(state, action) {
- if (!state) { /////проверка на первичность запуска !state
- try {
- return JSON.parse(localStorage[localStorageKey]);
- }
- catch { }
- }
- let res = originalReducer(state, action);
- localStorage[localStorageKey] = JSON.stringify(res);
- return res;
- }
- return wrapper
- }
- function promiseReducer(state = {}, action) { // диспетчер обработки
- if (action) {
- if (action.type === 'PROMISE') {
- let newState = { ...state };
- newState[action.name] = { status: action.status, payload: action.payload, error: action.error };
- return newState;
- }
- }
- return state;
- }
- function authReducer(state = {}, action) { // диспетчер обработки login
- if (action) {
- if (action.type === 'AUTH_LOGIN') {
- let newState = { ...state };
- newState.token = action.token;
- newState.payload = jwtDecode(action.token);
- if (!newState.payload) {
- newState.token = undefined;
- }
- if (newState.token)
- localStorage.authToken = newState.token;
- else
- delete localStorage.authToken;
- window.onhashchange();
- return newState;
- }
- else if (action.type === 'AUTH_LOGOUT') {
- let newState = { ...state };
- newState.token = undefined;
- newState.payload = undefined;
- delete localStorage.authToken;
- window.onhashchange();
- return newState;
- }
- }
- return state;
- }
- function createStore(reducer) {
- let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
- let cbs = [] //массив подписчиков
- const getState = () => { return state; } //функция, возвращающая переменную из замыкания
- const subscribe = cb => (cbs.push(cb), //запоминаем подписчиков в массиве
- () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
- function 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, //добавление функции getState в результирующий объект
- dispatch,
- subscribe //добавление subscribe в объект
- }
- }
- function getGql(url) {
- return function gql(query, vars = undefined) {
- try {
- let fetchSettings =
- {
- method: "POST",
- headers:
- {
- "Content-Type": "application/json",
- "Accept": "application/json"
- },
- body: JSON.stringify(
- {
- query: query,
- variables: vars
- })
- };
- let authToken = window.localStorage.authToken;
- if (authToken) {
- fetchSettings.headers["Authorization"] = `Bearer ${authToken}`;
- }
- return fetch(url, fetchSettings)
- .then(res => {
- try {
- if (!res.ok) {
- addErrorAlert(res.statusText);
- throw Error(res.statusText);
- }
- return res.json();
- }
- catch (error) {
- addErrorAlert(error.message);
- throw error;
- }
- });
- }
- catch (error) {
- addErrorAlert(error.message);
- throw error;
- }
- }
- }
- const gql = getGql("http://shop-roles.node.ed.asmer.org.ua/graphql");
- const actionPromise = (name, promise) => {
- return actionPromiseInt = async (dispatch) => {
- dispatch(actionPending(name)) //сигнализируем redux, что промис начался
- try {
- let payload = await promise //ожидаем промиса
- if (payload && payload.data)
- payload = Object.values(payload.data)[0];
- dispatch(actionFulfilled(name, payload)) //сигнализируем redux, что промис успешно выполнен
- return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
- }
- catch (error) {
- addErrorAlert(error.message);
- dispatch(actionRejected(name, error)) //в случае ошибки - сигнализируем redux, что промис несложился
- }
- }
- }
- const actionPending = (name) => ({ type: 'PROMISE', name: name, status: 'PENDING' });
- const actionFulfilled = (name, payload) => ({ type: 'PROMISE', name: name, payload: payload, status: 'FULFILLED' });
- const actionRejected = (name, error) => ({ type: 'PROMISE', name: name, error: error, status: 'REJECTED' });
- const actionAuthLogin = token => ({ type: 'AUTH_LOGIN', token });
- const actionAuthLogout = () => ({ type: 'AUTH_LOGOUT' });
- const actionCartAdd = (good, count = 1) => ({ type: 'CART_ADD', count: count, good: good });
- const actionCartSub = (good, count = 1) => ({ type: 'CART_SUB', count, good }); //Уменьшение количества товара. Должен уменьшать количество товара в state, или удалять его если количество будет 0 или отрицательным
- const actionCartDel = (good) => ({ type: 'CART_DEL', good }); //Удаление товара. Должен удалять ключ из state
- const actionCartSet = (good, count = 1) => ({ type: 'CART_SET', count, good }); //Задание количества товара. В отличие от добавления и уменьшения, не учитывает того количества, которое уже было в корзине, а тупо назначает количество поверху (или создает новый ключ, если в корзине товара не было). Если count 0 или отрицательное число - удаляем ключ из корзины;
- const actionCartShow = (good, count = 1) => ({ type: 'CART_SHOW', count, good }); //Задание количества товара. В отличие от добавления и уменьшения, не учитывает того количества, которое уже было в корзине, а тупо назначает количество поверху (или создает новый ключ, если в корзине товара не было). Если count 0 или отрицательное число - удаляем ключ из корзины;
- const actionCartClear = () => ({ type: 'CART_CLEAR' }); //Очистка корзины. state должен стать пустым объектом {}
- ///////////////////////////////////////
- const gqlRootCats = () => {
- const catQuery = `query roots {
- CategoryFind(query: "[{\\"parent\\": null }]") {
- _id name
- }}`;
- return gql(catQuery);
- }
- const actionRootCats = () =>
- actionPromise('rootCats', gqlRootCats());
- const gqlCategoryFindOne = (id) => {
- const catQuery = `query CategoryFindOne($q: String) {
- CategoryFindOne(query: $q) {
- _id name
- parent { _id name }
- subCategories { _id name }
- goods { _id name price description
- images { url }
- }
- }
- }`;
- return gql(catQuery, { q: `[{\"_id\": \"${id}\"}]` });
- }
- const actionCategoryFindOne = (id) =>
- actionPromise('catFindOne', gqlCategoryFindOne(id));
- const gqlGoodFindOne = (id) => {
- const catQuery = `
- query GoodFindOne($q: String) {
- GoodFindOne(query: $q) {
- _id name price description
- images { url }
- }
- }
- `;
- return gql(catQuery, { q: `[{\"_id\": \"${id}\"}]` });
- }
- const actionGoodFindOne = (id) =>
- actionPromise('goodFindOne', gqlGoodFindOne(id));
- //////////////////////////////////
- const actionLogin = (login, password) => {
- const upsertQuery = `query login($login:String, $password:String){
- login(login:$login, password:$password)
- }`;
- return gql(upsertQuery, { login: login, password: password });
- }
- const actionFullLogin = (login, password) => {
- return gqlFullLogin = async (dispatch) => {
- try {
- delete localStorage.authToken;
- //dispatch возвращает то, что вернул thunk, возвращаемый actionLogin, а там промис,
- //так как actionPromise возвращает асинхронную функцию
- let promiseResult = actionLogin(login, password);
- let res = await promiseResult;
- if (res && res.data) {
- let token = Object.values(res.data)[0];
- if (token && typeof token == 'string')
- return dispatch(actionAuthLogin(token));
- else
- addErrorAlert("User not found");
- }
- }
- catch (error) {
- addErrorAlert(error.message);
- throw error;
- }
- //проверьте что token - строка и отдайте его в actionAuthLogin
- }
- }
- ////////////////////////////////////////
- const actionAuthUpsert = (login, password) => {
- const loginQuery = `mutation UserRegistration($login: String, $password: String) {
- UserUpsert(user: {login: $login, password: $password}) {
- _id createdAt
- }
- }`;
- return gql(loginQuery, { login: login, password: password });////////
- }
- const actionFullAuthUpsert = (login, password) => {
- return gqlFullAuthUpsert = async (dispatch) => {
- //dispatch возвращает то, что вернул thunk, возвращаемый actionLogin, а там промис,
- //так как actionPromise возвращает асинхронную функцию
- delete localStorage.authToken;
- let promiseResult = actionAuthUpsert(login, password);
- let res = await promiseResult;
- dispatch(actionFullLogin(login, password));
- console.log(res)
- //проверьте что token - строка и отдайте его в actionAuthLogin
- }
- }
- ////////////////////////////////////////
- const orderUpsert = (order, id = null) => {
- const orderUpsertQuery = `mutation OrderUpsert($order: OrderInput) {
- OrderUpsert(order: $order) {
- _id
- }
- }`;
- return gql(orderUpsertQuery, { order: { "_id": id, "orderGoods": order } });
- }
- const orderFullUpsert = (then) => {
- return gqlFullOrderUpsert = async (dispatch, getState) => {
- let state = getState();
- let order = [];
- for (cartItem of Object.values(state.cartReducer)) {
- //{count: 3, good: {_id: "xxxx" }}
- order.push({ good: { _id: cartItem.good._id }, count: cartItem.count });
- }
- if (order.length > 0) {
- //dispatch возвращает то, что вернул thunk, возвращаемый actionLogin, а там промис,
- //так как actionPromise возвращает асинхронную функцию
- let promiseResult = orderUpsert(order);
- let res = await promiseResult;
- if (res && res.errors && res.errors.length > 0) {
- addErrorAlert(res.errors[0].message);
- throw res.errors[0];
- }
- dispatch(actionCartClear());
- }
- if (then)
- then();
- //проверьте что token - строка и отдайте его в actionAuthLogin
- }
- }
- const gqlFindOrders = () => {
- const findOrdersQuery = `query OrderFind {
- OrderFind(query: "[{}]") {
- _id total
- orderGoods {
- _id price count total createdAt
- good {
- name
- images { url }
- }
- }
- }
- }`;
- return gql(findOrdersQuery);
- }
- const actionFindOrders = () =>
- actionPromise('orders', gqlFindOrders());
- ///////////////////////////////////////////////////
- const store = createStore(combineReducers({ promiseReducer, authReducer, cartReducer: localStoredReducer(cartReducer, 'cart') }));
- const delay = (ms, action) => new Promise(ok => setTimeout(() => {
- action();
- ok(ms);
- }, ms));
- store.subscribe(() => {
- let state = store.getState();
- let cartItemsCount = 0;
- if (state.cartReducer) {
- let cartItems = Object.values(state.cartReducer);
- for (item of Object.values(state.cartReducer))
- cartItemsCount += item.count;
- }
- cartCountBadge.setAttribute("value", cartItemsCount);
- console.log({ state: store.getState() });
- });
- //////////////////////////////////////////////
- const addToCartBtn = (htmlEl, good) => {
- let btn = document.createElement("button");
- btn.innerText = "Add to Cart";
- btn.classList.add("ref-button");
- btn.classList.add("preview-toggle");
- btn.onclick = () => {
- store.dispatch(actionCartAdd(good));
- };
- htmlEl.appendChild(btn);
- return btn;
- }
- const fillRootCategories = (categories, htmlEl) => {
- htmlEl.innerText = '';
- if (!categories)
- return;
- let innerHtml = '';
- for (category of categories) {
- innerHtml += `<li class="h3"><a href="#/categories/${category._id}" class="nav-link text-dark">${category.name}</a></li>`;
- }
- htmlEl.innerHTML = innerHtml;
- }
- store.subscribe(() =>
- subscribePromiseItem("rootCats", categoriesList, [], fillRootCategories));
- const fillCurrentCategoryContent = (category, htmlEl) => {
- htmlEl.innerHTML = '';
- const { name, parent, subCategories, goods } = category;
- htmlEl.innerHTML = `<h1>${name}</h1>
- ${parent?.name ? `<section>Parent: <a href="#/subCategories/${parent?._id}">${parent.name}</a></section>` : ''}
- `
- if (subCategories?.length > 0) {
- htmlEl.innerHTML += '';//`<h3>Sub Categories:</h3><br>`
- for (const subCategory of subCategories) {
- htmlEl.innerHTML += `<h4><a href="#/subCategories/${subCategory._id}">${subCategory.name}</a><h4><hr/>`
- }
- }
- htmlEl.innerHTML += '';//`<section>Products:</section><br>`;
- addProductsList(goods, htmlEl)
- }
- store.subscribe(() =>
- subscribePromiseItem(
- "catFindOne", main, ["categories", "subCategories"], fillCurrentCategoryContent));
- const addProductsList = (goods, htmlEl) => {
- let outerDiv = document.createElement("div");
- outerDiv.innerHTML =
- `
- <div class="reflow-product-list">
- <div id="productsList" class="ref-products">
- </div>
- <div id="productsListPagination">
- </div>
- </div>
- `;
- htmlEl.appendChild(outerDiv);
- for (good of goods) {
- addProductToList(good, productsList);
- }
- new Pagination(productsList, productsListPagination, 5, listItemTag = 'div.ref-product')
- }
- const addProductToList = (good, htmlEl) => {
- let outerDiv = document.createElement('div');
- const { name, _id, price, description, images } = good;
- outerDiv.innerHTML =
- `
- <div id="good_${_id}" class="ref-product d-none">
- <div class="ref-media"><img class="ref-image"
- src="${getFullImageUrl(good.images[0])}" loading="lazy" /></div>
- <div class="ref-product-data">
- <div class="ref-product-info">
- <h5 class="ref-name"><a href="#/goods/${_id}">${name}</a></h5>
- <p class="ref-excerpt">${description}</p>
- </div><strong class="ref-price">$${price}</strong>
- </div>
- <div id="addCartDiv_${_id}" class="ref-addons"></div>
- </div>
- `;
- htmlEl.appendChild(outerDiv);
- let addCartDiv = document.getElementById(`addCartDiv_${_id}`);
- addToCartBtn(addCartDiv, good);
- }
- const fillCurrentGoodContent = (good, htmlEl) => {
- htmlEl.innerHTML = '';
- const { name, _id, price, description, images } = good;
- htmlEl.innerHTML = `<h1>${name}</h1>
- <section>Description: ${description}</section>
- <section>Price: ${price}</section>
- `;
- htmlEl.innerHTML += `<section>Images:</section><br>` //вставить css display block
- for (const image of images) {
- htmlEl.innerHTML += `<img width="170px" src="${"http://shop-roles.node.ed.asmer.org.ua/"}${image.url}"</img><br>`//вставить css display block
- }
- addToCartBtn(htmlEl, good);
- }
- store.subscribe(() =>
- subscribePromiseItem(
- "goodFindOne", main, ["goods"], fillCurrentGoodContent));
- const subscribePromiseItem = (promiseName, htmlEl, subscrNames, execFunc) => {
- const [, route, _id] = location.hash.split('/');
- if ((subscrNames.length > 0 && (!route || !subscrNames.some(v => v == route)))/* || !_id*/)
- return;
- let reducerData = store.getState().promiseReducer[promiseName];
- if (!reducerData)
- return;
- const { status, payload, error } = reducerData;
- if (status === 'PENDING') {
- htmlEl.innerHTML = `<img src='https://cdn.dribbble.com/users/63485/screenshots/1309731/infinite-gif-preloader.gif' />`
- }
- else if (status == "FULFILLED") {
- execFunc(payload, htmlEl);
- }
- else if (status == "FULFILLED") {
- addErrorAlert(error.message);
- }
- }
- const getFullImageUrl = (image) =>
- `http://shop-roles.node.ed.asmer.org.ua/${image?.url}`;
- const showCartContent = (cart, htmlEl) => {
- htmlEl.innerHTML = '';
- htmlEl.innerHTML = `<h1>Cart</h1>
- `;
- htmlEl.innerHTML += '';//`<section>Items:</section><br>` //вставить css display block
- let allCount = 0;
- let htmlContent = '';
- for (const item of Object.values(cart)) {
- let { count, good } = item;
- let inpId = `inp_${good._id}`;
- let delBtnId = `delBtn_${good._id}`;
- htmlContent += `
- <div>
- <img width="170px" src="${getFullImageUrl(good.images[0])}"</img>
- <a href="#/goods/${good._id}">${good.name}</a>
- <input type="number" min="1" max="999" id="${inpId}" value="${count}">
- <button class="ref-button preview-toggle" id="${delBtnId}">Remove</button>
- </div>
- <br>`//вставить css display block
- allCount += count;
- }
- htmlContent += `<div>Count ${allCount}</div><br>`;
- htmlContent += `<button class="ref-button preview-toggle" id="btnCheckout">Checkout</button><br>`;
- htmlEl.innerHTML += htmlContent;
- for (const item of Object.values(cart)) {
- let { good } = item;
- let inpId = `inp_${good._id}`;
- let delBtnId = `delBtn_${good._id}`;
- let inp = document.getElementById(inpId);
- inp.addEventListener("change", function (e) { store.dispatch(actionCartSet(good, +inp.value)); });
- let delBtn = document.getElementById(delBtnId);
- delBtn.addEventListener("click", function (e) { store.dispatch(actionCartDel(good)); });
- }
- let btnCheckout = document.getElementById("btnCheckout");
- btnCheckout.addEventListener("click", function (e) {
- window.location = "#/checkout/";
- });
- }
- store.subscribe(() =>
- subscribeSimple(
- "cartReducer", main, ["cart"], showCartContent));
- const showOrder = (order, num, htmlEl) => {
- let htmlContent = `<div class="order d-none"><h2>Order: #${num}</h2>
- <!--<div>Created on: ${order.createdAt}</div>-->
- `;
- htmlContent += `<h3>Items:</h3>` //вставить css display block
- let orderGoods = Object.values(order.orderGoods);
- for (let i = 0; i < orderGoods.length; i++) {
- let { order, count, price, total, good } = orderGoods[i];
- htmlContent +=
- `
- <div class="ref-product align-items-center">
- <strong class="ref-count">${i}. </strong>
- <div class="ref-media"><img class="ref-image" width="170px" src="${getFullImageUrl(good.images[0])}"</img></div>
- <div class="ref-product-data">
- <div class="ref-product-info">
- <h5 class="ref-name"><a href="#/goods/${good._id}">${good.name}</a></h5>
- </div>
- <strong class="ref-price">Price: ${price}</strong>
- <strong class="ref-count">Count: ${count}</strong>
- <strong class="ref-price">Total: ${total}</strong>
- </div>
- </div>
- `//вставить css display block
- }
- htmlContent += `<h3 class="ref-count">Total ${order.total}</h3><hr/></div>`;
- htmlEl.innerHTML += htmlContent;
- }
- const showOrders = (orders, htmlEl) => {
- htmlEl.innerHTML = "<header>Orders:</header>";
- if (!localStorage?.authToken)
- return;
- if (orders) {
- let ordersContainerDiv = document.createElement("div");
- ordersContainerDiv.classList.add("container", "reflow-product-list");
- let ordersDiv = document.createElement("div");
- ordersContainerDiv.appendChild(ordersDiv);
- ordersDiv.classList.add("ref-products");
- for (let i = orders.length; i > 0; i--) {
- let order = orders[i - 1];
- showOrder(order, i, ordersDiv);
- }
- let paginationDiv = document.createElement("div");
- htmlEl.append(ordersContainerDiv, paginationDiv);
- new Pagination(ordersDiv, paginationDiv, 5, listItemTag = 'div.order')
- }
- }
- store.subscribe(() =>
- subscribePromiseItem(
- "orders", main, ["orders"], showOrders));
- const subscribeSimple = (reducerName, htmlEl, subscrNames, execFunc) => {
- const [, route, _id] = location.hash.split('/');
- if (!subscrNames || !subscrNames.some(v => v == route))
- return;
- let reducerData = store.getState()[reducerName];
- execFunc(reducerData, htmlEl);
- }
- window.onhashchange = () => {
- const [, route, _id] = location.hash.split('/')
- const routes = {
- categories() {
- console.log('Category', _id)
- store.dispatch(actionCategoryFindOne(_id))
- },
- subCategories() {
- console.log('subCategory', _id)
- store.dispatch(actionCategoryFindOne(_id))
- },
- goods() {
- console.log('good', _id)
- store.dispatch(actionGoodFindOne(_id))
- },
- cart() {
- console.log('cart')
- store.dispatch(actionCartShow())
- },
- checkout() {
- console.log('checkout');
- let state = store.getState();
- if (routes.login())
- store.dispatch(orderFullUpsert(() => window.location = "#/orders/"));
- },
- orders() {
- if (!localStorage.authToken) {
- showOrders([], main);
- return;
- }
- console.log('order');
- store.dispatch(actionFindOrders());
- },
- login() {
- if (!localStorage.authToken) {
- main.innerText = '';
- const form = new LoginForm(main);
- form.onLogin = (login, password) => {
- store.dispatch(actionFullLogin(login, password));
- }
- return false;
- }
- else if (route == "login") {
- window.location = "#/";
- window.location.reload();
- }
- return true;
- },
- register() {
- if (!localStorage.authToken) {
- main.innerText = '';
- const form = new LoginForm(main);
- form.onLogin = (login, password) => {
- store.dispatch(actionFullAuthUpsert(login, password));
- }
- return false;
- }
- else if (route == "register") {
- window.location = "#/";
- window.location.reload();
- }
- return true;
- },
- logout() {
- if (localStorage.authToken) {
- store.dispatch(actionAuthLogout());
- window.location = "#/login/";
- window.location.reload();
- }
- },
- //register(){
- ////нарисовать форму регистрации, которая по нажатию кнопки Login делает store.dispatch(actionFullRegister(login, password))
- //},
- }
- if (localStorage.authToken) {
- loginLink.classList.add('d-none');
- regLink.classList.add('d-none');
- historyLink.classList.remove('d-none');
- logoutLink.classList.remove('d-none');
- }
- else {
- loginLink.classList.remove('d-none');
- regLink.classList.remove('d-none');
- historyLink.classList.add('d-none');
- logoutLink.classList.add('d-none');
- }
- if (route in routes) {
- routes[route]()
- }
- }
- window.onhashchange()
- store.dispatch(actionRootCats());
- /*store.dispatch(actionCategoryFindOne("6262ca7dbf8b206433f5b3d1"));
- store.dispatch(actionGoodFindOne("62d3099ab74e1f5f2ec1a125"));
- store.dispatch(actionFullLogin("Berg", "123456789"));
- //store.dispatch(actionFullAuthUpsert("Berg1", "12345678911"));
- store.dispatch(actionCartAdd({ _id: '62d30938b74e1f5f2ec1a124', price: 50 }));
- delay(3000, () => {
- store.dispatch(orderFullUpsert());
- store.dispatch(actionFindOrders());
- });*/
- //delay(500, () => store.dispatch(actionFindOrders()));
- </script>
- </body>
|