index.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. function createStore(reducer) {
  2. let state = reducer(undefined, {}); //стартовая инициализация состояния, запуск редьюсера со state === undefined
  3. let cbs = []; //массив подписчиков
  4. const getState = () => state; //функция, возвращающая переменную из замыкания
  5. const subscribe = (cb) => (
  6. cbs.push(cb), //запоминаем подписчиков в массиве
  7. () => (cbs = cbs.filter((c) => c !== cb))
  8. ); //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
  9. const dispatch = (action) => {
  10. if (typeof action === "function") {
  11. //если action - не объект, а функция
  12. return action(dispatch, getState); //запускаем эту функцию и даем ей dispatch и getState для работы
  13. }
  14. const newState = reducer(state, action); //пробуем запустить редьюсер
  15. if (newState !== state) {
  16. //проверяем, смог ли редьюсер обработать action
  17. state = newState; //если смог, то обновляем state
  18. for (let cb of cbs) cb(); //и запускаем подписчиков
  19. }
  20. };
  21. return {
  22. getState, //добавление функции getState в результирующий объект
  23. dispatch,
  24. subscribe, //добавление subscribe в объект
  25. };
  26. }
  27. function promiseReducer(state = {}, { type, name, status, payload, error }) {
  28. if (type === "PROMISE") {
  29. return {
  30. ...state,
  31. [name]: { status, payload, error },
  32. };
  33. }
  34. return state;
  35. }
  36. const actionPending = (name) => ({ type: "PROMISE", name, status: "PENDING" });
  37. const actionFulfilled = (name, payload) => ({
  38. type: "PROMISE",
  39. name,
  40. status: "FULFILLED",
  41. payload,
  42. });
  43. const actionRejected = (name, error) => ({
  44. type: "PROMISE",
  45. name,
  46. status: "REJECTED",
  47. error,
  48. });
  49. const actionPromise = (name, promise) => async (dispatch) => {
  50. dispatch(actionPending(name));
  51. try {
  52. let payload = await promise;
  53. dispatch(actionFulfilled(name, payload));
  54. return payload;
  55. } catch (error) {
  56. dispatch(actionRejected(name, error));
  57. }
  58. };
  59. const getGQL = (url) => (query, variables) =>
  60. fetch(url, {
  61. method: "POST",
  62. headers: {
  63. "Content-Type": "application/json",
  64. ...(localStorage.authToken
  65. ? { Authorization: "Bearer " + localStorage.authToken }
  66. : {}),
  67. },
  68. body: JSON.stringify({ query, variables }),
  69. })
  70. .then((res) => res.json())
  71. .then((data) => {
  72. if (data.data) {
  73. return Object.values(data.data)[0];
  74. } else throw new Error(JSON.stringify(data.errors));
  75. });
  76. const backendURL = "http://shop-roles.asmer.fs.a-level.com.ua";
  77. const gql = getGQL(backendURL + "/graphql");
  78. const actionRootCats = () =>
  79. actionPromise(
  80. "rootCats",
  81. gql(`query {
  82. CategoryFind(query: "[{\\"parent\\":null}]"){
  83. _id name
  84. }
  85. }`)
  86. );
  87. const actionCatById = (_id) =>
  88. actionPromise(
  89. "catById",
  90. gql(
  91. `query catById($q: String){
  92. CategoryFindOne(query: $q){
  93. _id name goods {
  94. _id name price images {
  95. url
  96. }
  97. }
  98. subCategories {
  99. _id name
  100. }
  101. parent {
  102. _id name
  103. }
  104. }
  105. }`,
  106. { q: JSON.stringify([{ _id }]) }
  107. )
  108. );
  109. const actionGoodById = (_id) =>
  110. actionPromise(
  111. "goodById",
  112. gql(
  113. `query goodById($good: String){
  114. GoodFindOne(query: $good){
  115. _id name description price categories{_id name owner{_id login nick}}images{url}
  116. }
  117. }`,
  118. { good: JSON.stringify([{ _id }]) }
  119. )
  120. );
  121. const actionOrders = () =>
  122. actionPromise(
  123. "orders",
  124. gql(`
  125. query orders {
  126. OrderFind(query: "[{}]") {
  127. _id
  128. total
  129. createdAt
  130. orderGoods {
  131. price
  132. count
  133. total
  134. good {
  135. name
  136. categories {
  137. name
  138. }
  139. }
  140. }
  141. }
  142. }`)
  143. );
  144. const jwtDecode = (token) => {
  145. try {
  146. const payload = JSON.parse(atob(token.split(".")[1]));
  147. // серединка, atob, JSON.parse
  148. return payload;
  149. } catch (e) {
  150. console.log(e);
  151. }
  152. };
  153. function authReducer(state, { type, token }) {
  154. if (state === undefined && localStorage.authToken) {
  155. token = localStorage.authToken;
  156. type = "AUTH_LOGIN";
  157. }
  158. if (type === "AUTH_LOGIN") {
  159. let decodeToken = jwtDecode(token);
  160. if (decodeToken) {
  161. localStorage.authToken = token;
  162. return {
  163. token,
  164. payload: decodeToken,
  165. };
  166. }
  167. }
  168. if (type === "AUTH_LOGOUT") {
  169. localStorage.authToken = "";
  170. //чистим localStorage.authToken
  171. return {};
  172. }
  173. return state || {};
  174. }
  175. //написать к этому пару экшонов
  176. const actionAuthLogin = (token) => ({ type: "AUTH_LOGIN", token });
  177. const actionAuthLogout = () => ({ type: "AUTH_LOGOUT" });
  178. function cartReducer(state = {}, { type, good = {}, count = 1 }) {
  179. //каков state:
  180. //{
  181. // _id1: {count:1, good: {_id1, name, price, images}}
  182. // _id2: {count:1, good: {_id2, name, price, images}}
  183. //}
  184. //каковы действия по изменению state
  185. if (type === "CART_ADD") {
  186. count = +count;
  187. if (!count) return state;
  188. else
  189. return {
  190. ...state,
  191. [good._id]: { good, count: count + (state[good._id]?.count || 0) },
  192. };
  193. }
  194. if (type === "CART_CHANGE") {
  195. count = +count;
  196. if (!count) return state;
  197. return {
  198. ...state,
  199. [good._id]: { good, count },
  200. };
  201. }
  202. if (type === "CART_DELETE") {
  203. const { [good._id]: removedProperty, ...someGoods } = state;
  204. return someGoods;
  205. }
  206. if (type === "CART_CLEAR") {
  207. return {};
  208. }
  209. return state;
  210. }
  211. const actionCartAdd = (good, count = 1) => ({ type: "CART_ADD", good, count });
  212. const actionCartChange = (good, count = 1) => ({
  213. type: "CART_CHANGE",
  214. good,
  215. count,
  216. });
  217. const actionCartDelete = (good) => ({ type: "CART_DELETE", good });
  218. const actionCartClear = () => ({ type: "CART_CLEAR" });
  219. const actionOrder = () => async (dispatch, getState) => {
  220. let { cart } = getState();
  221. const orderGoods = Object.entries(cart).map(([_id, { count }]) => ({
  222. good: { _id },
  223. count,
  224. }));
  225. let result = await dispatch(
  226. actionPromise(
  227. "order",
  228. gql(
  229. `
  230. mutation newOrder($order:OrderInput){
  231. OrderUpsert(order:$order)
  232. { _id total }
  233. }
  234. `,
  235. { order: { orderGoods: orderGoods } }
  236. )
  237. )
  238. );
  239. if (result?._id) {
  240. dispatch(actionCartClear());
  241. }
  242. };
  243. const combineReducers =
  244. (reducers) =>
  245. (state = {}, action) => {
  246. const newState = {};
  247. for (const [reducerName, reducer] of Object.entries(reducers)) {
  248. let newSubState = reducer(state[reducerName], action);
  249. // console.log(newSubState)
  250. if (newSubState !== state[reducerName]) {
  251. newState[reducerName] = newSubState;
  252. // console.log(newState[reducerName])
  253. }
  254. }
  255. if (Object.keys(newState).length !== 0) return { ...state, ...newState };
  256. else return state;
  257. };
  258. const store = createStore(
  259. combineReducers({
  260. promise: promiseReducer,
  261. auth: authReducer,
  262. cart: cartReducer,
  263. })
  264. );
  265. // для корневых категорий
  266. store.dispatch(actionRootCats());
  267. store.subscribe(() => console.log(store.getState()));
  268. const actionFullLogin = (login, password) => async (dispatch) => {
  269. let token = await dispatch(
  270. actionPromise(
  271. "auth",
  272. gql(
  273. ` query login($login:String, $password:String){
  274. login(login:$login, password:$password)} `,
  275. { login, password }
  276. )
  277. )
  278. );
  279. if (token) {
  280. dispatch(actionAuthLogin(token));
  281. }
  282. };
  283. const actionRegister = (login, password) =>
  284. actionPromise(
  285. "register",
  286. gql(
  287. `mutation register($login: String, $password: String) {
  288. UserUpsert(user: {login: $login, password: $password, nick: $login}) {
  289. _id login
  290. }
  291. }`,
  292. { login: login, password: password }
  293. )
  294. );
  295. const actionFullRegister = (login, password) => async (dispatch) => {
  296. let tokenCheck = await dispatch(actionRegister(login, password));
  297. if (tokenCheck?.login === login) {
  298. dispatch(actionFullLogin(login, password));
  299. }
  300. };
  301. store.subscribe(() => {
  302. const { rootCats } = store.getState().promise;
  303. if (rootCats?.payload) {
  304. aside.innerHTML = "";
  305. for (const { _id, name } of rootCats.payload) {
  306. const link = document.createElement("a");
  307. link.href = `#/category/${_id}`;
  308. link.innerText = name;
  309. aside.append(link);
  310. }
  311. }
  312. });
  313. store.subscribe(() => {
  314. const { catById } = store.getState().promise;
  315. const { cart } = store.getState();
  316. const [, route, _id] = location.hash.split("/");
  317. //проверка на наличие 'category' в адресной строке
  318. if (catById?.payload && route === "category") {
  319. //достаем имя
  320. const { name } = catById.payload;
  321. main.innerHTML = `<h1>${name}</h1>`;
  322. if (catById?.payload?.subCategories) {
  323. for (const { _id, name } of catById.payload?.subCategories) {
  324. const link = document.createElement("a");
  325. link.href = `#/category/${_id}`;
  326. link.innerText = name;
  327. main.append(link);
  328. }
  329. }
  330. for (const myGood of catById.payload.goods) {
  331. const { _id, name, price, images } = myGood;
  332. const card = document.createElement("div");
  333. card.innerHTML = `<h2>${name}</h2>
  334. <img src="${backendURL}/${images[0].url}" />
  335. <br>
  336. <strong> Цена ${price} грн</strong>
  337. <br>
  338. <a href ="#/good/${_id}">Перейти на товар ${name} </a>`;
  339. let btnAdd = document.createElement("button");
  340. btnAdd.innerText = "Добавить в корзину";
  341. card.append(btnAdd);
  342. let btnDelete = document.createElement("button");
  343. btnDelete.innerText = "Удалить из корзины";
  344. btnDelete.classList.add("deleteBtn");
  345. card.append(btnDelete);
  346. let p = document.createElement("p");
  347. if (cart[myGood._id]?.count != undefined)
  348. p.innerHTML = `Выбранное количество: ${cart[myGood._id]?.count}`;
  349. else p.innerHTML = `Выбранное количество: 0`;
  350. card.append(p);
  351. // console.log(cart[myGood._id]?.count)
  352. //console.log(count)
  353. btnAdd.onclick = () => {
  354. store.dispatch(actionCartAdd(myGood));
  355. };
  356. btnDelete.onclick = () => {
  357. store.dispatch(actionCartDelete(myGood));
  358. };
  359. main.append(card);
  360. }
  361. if (catById.payload?.parent && catById.payload?.parent != null) {
  362. const { _id, name } = catById.payload.parent;
  363. const linkParent = document.createElement("a");
  364. linkParent.href = `#/category/${_id}`;
  365. //console.log(_id, name);
  366. linkParent.innerText = ` Вернуться к категории ` + name;
  367. main.append(linkParent);
  368. }
  369. }
  370. });
  371. store.subscribe(() => {
  372. const { goodById } = store.getState().promise;
  373. const { cart } = store.getState();
  374. const [, route, _id] = location.hash.split("/");
  375. //проверка на наличие 'good' в адресной строке
  376. if (goodById?.payload && route === "good") {
  377. main.innerHTML = "";
  378. //достаем имя
  379. const { _id, name, description, price, images } = goodById.payload;
  380. main.innerHTML = `<h1>${name}</h1>
  381. <img src="${backendURL}/${images[0].url}" />
  382. <h3>${description} </h3>
  383. <br>
  384. <strong> Цена ${price} грн </strong>`;
  385. let btnAdd = document.createElement("button");
  386. btnAdd.innerText = "Добавить в корзину";
  387. btnAdd.onclick = () => store.dispatch(actionCartAdd(goodById.payload));
  388. main.append(btnAdd);
  389. let p = document.createElement("p");
  390. //console.log(goodById.payload._id);
  391. if (cart[goodById.payload._id]?.count != undefined)
  392. p.innerHTML = `Выбранное количество: ${
  393. cart[goodById.payload._id]?.count
  394. }`;
  395. else p.innerHTML = `Выбранное количество: 0`;
  396. main.append(p);
  397. if (goodById.payload?.categories) {
  398. for (const { _id, name } of goodById.payload.categories) {
  399. const link = document.createElement("a");
  400. link.href = `#/category/${_id}`;
  401. link.innerText = `Вернуться к категории ${name}`;
  402. main.append(link);
  403. }
  404. }
  405. }
  406. });
  407. let pMess = document.createElement("p");
  408. store.subscribe(() => {
  409. const { auth } = store.getState();
  410. if (Object.keys(auth).length !== 0) {
  411. btnSignIn.innerHTML = `${auth.payload.sub.login}`;
  412. }
  413. const [, route, _id] = location.hash.split("/");
  414. if (route === "login") {
  415. pMess.innerHTML = "";
  416. if (Object.keys(auth).length !== 0) {
  417. if (
  418. actionFulfilled(auth, auth.payload != null) &&
  419. actionAuthLogin(auth)
  420. ) {
  421. pMess.innerHTML =
  422. "Вы авторизировались! Добро пожаловать, " +
  423. `${auth.payload.sub.login}, в наш магазин!`;
  424. pMess.style.backgroundColor = "#98FB98";
  425. pMess.style.color = "#006400";
  426. main.prepend(pMess);
  427. }
  428. } else {
  429. btnSignIn.innerHTML = "Sign In";
  430. pMess.innerHTML = "Имя пользователя или пароль неверны.";
  431. pMess.style.backgroundColor = "#FA8072";
  432. pMess.style.color = "#8B0000";
  433. main.prepend(pMess);
  434. }
  435. }
  436. });
  437. store.subscribe(() => {
  438. const { auth } = store.getState();
  439. const [, route, _id] = location.hash.split("/");
  440. if (route === "register") {
  441. pMess.innerHTML = "";
  442. if (Object.keys(auth).length !== 0) {
  443. if (
  444. actionFulfilled(auth, auth.payload != null) &&
  445. actionAuthLogin(auth)
  446. ) {
  447. pMess.innerHTML = `Вы успешно зарегистрированы!Добро пожаловать, ${auth.payload.sub.login}, в наш магазин!`;
  448. pMess.style.backgroundColor = "#98FB98";
  449. pMess.style.color = "#006400";
  450. btnSignIn.innerHTML = `${auth.payload.sub.login}`;
  451. main.prepend(pMess);
  452. }
  453. } else {
  454. btnSignIn.innerHTML = "Sign In";
  455. pMess.innerHTML =
  456. "Такое имя пользователя уже существует, придумайте другое!";
  457. pMess.style.backgroundColor = "#FA8072";
  458. pMess.style.color = "#8B0000";
  459. main.prepend(pMess);
  460. }
  461. }
  462. });
  463. store.subscribe(() => {
  464. const { orders } = store.getState().promise;
  465. const [, route, _id] = location.hash.split("/");
  466. if (route === "dashboard") {
  467. main.innerHTML = "";
  468. let h2 = document.createElement("h2");
  469. if (orders != undefined && orders?.payload?.length != 0) {
  470. //console.log(orders);
  471. h2.innerHTML = "Ваши заказы: ";
  472. main.append(h2);
  473. for (let elem in orders?.payload) {
  474. const card = document.createElement("div");
  475. card.classList.add("cart");
  476. let num = document.createElement("p");
  477. num.innerHTML = `<h1>№${elem} заказа
  478. <br>
  479. Ваши товары:
  480. </h1>`;
  481. card.append(num);
  482. //console.log(orders?.payload[elem].total)
  483. for (let elem2 in orders?.payload[elem]?.orderGoods) {
  484. card.innerHTML += `<h2> * ${orders?.payload[elem]?.orderGoods[elem2].good?.name}</h2>
  485. <h3> Цена: ${orders?.payload[elem]?.orderGoods[elem2]?.price} </h3>
  486. <h3> Количество: ${orders?.payload[elem]?.orderGoods[elem2]?.count} </h3>
  487. `;
  488. // console.log(orders?.payload[elem]?.orderGoods[elem2].good?.name);
  489. }
  490. main.append(card);
  491. let num2 = document.createElement("p");
  492. num2.innerHTML = `<span> Общая сумма заказа: ${orders?.payload[elem].total} </span>`;
  493. card.append(num2);
  494. }
  495. } else {
  496. h2.innerHTML = "У вас нету еще оформленных заказов! :( ";
  497. main.append(h2);
  498. }
  499. }
  500. });
  501. let btnSignIn = document.getElementById("signIn");
  502. let btnlogOut = document.getElementById("logOut");
  503. window.onhashchange = () => {
  504. const [, route, _id] = location.hash.split("/");
  505. const routes = {
  506. category() {
  507. store.dispatch(actionCatById(_id));
  508. },
  509. good() {
  510. store.dispatch(actionGoodById(_id));
  511. },
  512. login() {
  513. main.innerHTML = "";
  514. let labelLog = document.createElement("label");
  515. labelLog.innerHTML = "Login";
  516. let inputLogin = document.createElement("input");
  517. let labelPass = document.createElement("label");
  518. labelPass.innerHTML = "Password";
  519. let inputPassword = document.createElement("input");
  520. let sign = document.createElement("button");
  521. sign.innerHTML = "SIGN";
  522. sign.setAttribute("id", "sign");
  523. pMess.innerHTML = "";
  524. main.append(labelLog);
  525. main.append(inputLogin);
  526. main.append(labelPass);
  527. main.append(inputPassword);
  528. main.append(sign);
  529. sign.addEventListener("click", () => {
  530. try {
  531. if (inputLogin.value != "" && inputPassword.value != "") {
  532. store.dispatch(
  533. actionFullLogin(inputLogin.value, inputPassword.value)
  534. );
  535. } else {
  536. pMess.innerHTML = "Введите значение!";
  537. pMess.style.backgroundColor = "#FA8072";
  538. pMess.style.color = "#8B0000";
  539. main.prepend(pMess);
  540. }
  541. } catch (e) {
  542. console.log("myError", e);
  543. }
  544. });
  545. },
  546. register() {
  547. main.innerHTML = "";
  548. let labelLog = document.createElement("label");
  549. labelLog.innerHTML = "Login";
  550. let inputLogin = document.createElement("input");
  551. let labelPass = document.createElement("label");
  552. labelPass.innerHTML = "Password";
  553. let inputPassword = document.createElement("input");
  554. let register = document.createElement("button");
  555. register.innerHTML = "REGISTER";
  556. pMess.innerHTML = "";
  557. main.append(pMess);
  558. main.append(labelLog);
  559. main.append(inputLogin);
  560. main.append(labelPass);
  561. main.append(inputPassword);
  562. register.addEventListener("click", () => {
  563. try {
  564. if (inputLogin.value != "" && inputPassword.value != "") {
  565. store.dispatch(
  566. actionFullRegister(inputLogin.value, inputPassword.value)
  567. );
  568. } else {
  569. pMess.innerHTML = "Введите значение!";
  570. pMess.style.backgroundColor = "#FA8072";
  571. pMess.style.color = "#8B0000";
  572. }
  573. } catch (e) {
  574. console.log("myError", e);
  575. }
  576. });
  577. main.append(register);
  578. },
  579. logout() {
  580. main.innerHTML = "";
  581. store.dispatch(actionAuthLogout());
  582. btnSignIn.innerHTML = "Sign In";
  583. },
  584. cart() {
  585. const { cart } = store.getState();
  586. main.innerHTML = "";
  587. let label = document.createElement("h2");
  588. label.innerHTML = "";
  589. main.append(label);
  590. if (Object.keys(cart).length !== 0) {
  591. label.innerHTML = "Ваши товары: ";
  592. for (let good in cart) {
  593. let {
  594. good: {
  595. _id: _id,
  596. name: name,
  597. price: price,
  598. images: [{ url }],
  599. },
  600. count,
  601. } = cart[good];
  602. const card = document.createElement("div");
  603. card.classList.add("cart");
  604. card.innerHTML = `
  605. <img src="${backendURL}/${url}" class="forCart" />
  606. <h2>${name}</h2>
  607. <a href ="#/good/${_id}">Перейти на товар ${name} </a>
  608. `;
  609. let inputCount = document.createElement("input");
  610. inputCount.setAttribute("type", "number");
  611. inputCount.value = `${count}`;
  612. inputCount.min = 1;
  613. let strongPrice = document.createElement("strong");
  614. strongPrice.innerHTML = ` Цена ${price * inputCount.value} грн `;
  615. inputCount.oninput = function () {
  616. store.dispatch(actionCartChange(cart[good].good, inputCount.value));
  617. strongPrice.innerHTML = ` Цена ${price * inputCount.value} грн `;
  618. };
  619. inputCount.classList.add("count");
  620. let btnDelete = document.createElement("button");
  621. btnDelete.innerHTML = "Удалить товар";
  622. btnDelete.classList.add("deleteBtn");
  623. btnDelete.addEventListener("click", () => {
  624. store.dispatch(actionCartDelete(cart[good].good));
  625. card.innerHTML = "";
  626. });
  627. inputCount.oninput = function () {
  628. store.dispatch(actionCartChange(cart[good].good, inputCount.value));
  629. strongPrice.innerHTML = ` Цена ${price * inputCount.value} грн `;
  630. };
  631. card.append(strongPrice);
  632. card.append(inputCount);
  633. card.append(btnDelete);
  634. main.append(card);
  635. }
  636. let btnClear = document.createElement("button");
  637. btnClear.innerHTML = "Очистить все товары";
  638. btnClear.classList.add("clear");
  639. let btnOrder = document.createElement("button");
  640. btnOrder.innerHTML = "Оформить заказ";
  641. btnOrder.classList.add("order");
  642. btnOrder.addEventListener("click", () => {
  643. alert("Ваш заказ оформлен! Спасибо что выбираете наш магазин!");
  644. main.innerHTML = "";
  645. label.innerHTML = "Ваша корзина пустая!";
  646. main.append(label);
  647. store.dispatch(actionOrder());
  648. });
  649. btnClear.addEventListener("click", () => {
  650. store.dispatch(actionCartClear());
  651. main.innerHTML = "";
  652. label.innerHTML = "Ваша корзина пустая!";
  653. main.append(label);
  654. });
  655. main.append(btnClear);
  656. main.append(btnOrder);
  657. } else {
  658. label.innerHTML = "Ваша корзина пустая!";
  659. }
  660. },
  661. dashboard() {
  662. store.dispatch(actionOrders());
  663. },
  664. };
  665. if (route in routes) routes[route]();
  666. };
  667. window.onhashchange();