index.js 22 KB

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