main.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. //debugger;
  2. function createStore(reducer){
  3. let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
  4. let cbs = [] //массив подписчиков
  5. const getState = () => state //функция, возвращающая переменную из замыкания
  6. const subscribe = cb => (cbs.push(cb), //запоминаем подписчиков в массиве
  7. () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
  8. const dispatch = action => {
  9. if (typeof action === 'function'){ //если action - не объект, а функция
  10. return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы
  11. }
  12. const newState = reducer(state, action) //пробуем запустить редьюсер
  13. if (newState !== state){ //проверяем, смог ли редьюсер обработать action
  14. state = newState //если смог, то обновляем state
  15. for (let cb of cbs) cb() //и запускаем подписчиков
  16. }
  17. }
  18. return {
  19. getState, //добавление функции getState в результирующий объект
  20. dispatch,
  21. subscribe //добавление subscribe в объект
  22. }
  23. }
  24. function promiseReducer(state={}, {type, name, status, payload, error}){
  25. // {
  26. // login: {status, payload, error}
  27. // catById: {status, payload, error}
  28. // }
  29. if (type === 'PROMISE'){
  30. return {
  31. ...state,
  32. [name]:{status, payload, error}
  33. }
  34. }
  35. return state
  36. }
  37. //const store = createStore(promiseReducer);
  38. function jwtDecode(token){
  39. try {let base64Url = token.split('.')[1];
  40. let base64 = atob(base64Url);
  41. return JSON.parse(base64);
  42. } catch { (err) =>
  43. console.log(err);
  44. }
  45. //раскодировать токен:
  46. //выкусить середочку
  47. //atob
  48. //JSON.parse
  49. //на любом этапе могут быть исключения
  50. }
  51. function authReducer(state, {type, token}){
  52. if (!state){
  53. if(localStorage.authToken){ //проверить localStorage.authToken на наличие
  54. return {
  55. 'type': 'AUTH_LOGIN',
  56. 'token':localStorage.authToken,
  57. }; //если есть - сделать так, что бы следующий if сработал
  58. } else { return state = {}} //если нет - вернуть {}
  59. }
  60. if (type === 'AUTH_LOGIN'){
  61. const bigToken = jwtDecode(token); //взять токен из action
  62. if (bigToken) { //попытаться его jwtDecode
  63. localStorage.setItem('authToken', token); //если удалось, то:
  64. return {
  65. token,
  66. payload: bigToken,
  67. }
  68. } //сохранить токен в localStorage
  69. } //вернуть объект вида {токен, payload: раскодированный токен}
  70. if (type === 'AUTH_LOGOUT'){
  71. localStorage.clear(); //почистить localStorage
  72. return {}; //вернуть пустой объект
  73. }
  74. return state
  75. }
  76. const actionAuthLogin = token => ({type: 'AUTH_LOGIN', token})
  77. const actionAuthLogout = () => ({type: 'AUTH_LOGOUT'})
  78. //const store = createStore(authReducer)
  79. function cartReducer(state={}, {type, good = {}, count=1}){
  80. //{
  81. // _id1: {good, count}
  82. // _id2: {good, count}
  83. //}
  84. const types = {
  85. CART_ADD(){ //как CHANGE, только если ключ раньше был, то достать из count и добавить
  86. const {_id} = good;
  87. //к count из action. Если не было, достать 0 и добавить к count из action
  88. return {
  89. ...state,
  90. [_id]:{good, count:count +(state[_id]?.count || 0)}
  91. }
  92. },
  93. CART_MINUS(){ //как CHANGE, только если ключ раньше был, то достать из count и добавить
  94. const {_id} = good;
  95. //к count из action. Если не было, достать 0 и добавить к count из action
  96. return {
  97. ...state,
  98. [_id]:{good, count: (-count +(state[_id]?.count || 0)) < 1 ? 0: -count +(state[_id]?.count || 0)}
  99. }
  100. },
  101. CART_REMOVE(){ //смочь скопировать объект и выкинуть ключ. как вариант через
  102. //деструктуризацию
  103. const {_id} = good;
  104. let newState = {...state};
  105. const arrKeys = Object.keys(newState);
  106. for (let key of arrKeys) {
  107. if(_id === key) {
  108. delete newState[_id];
  109. }
  110. }
  111. return {
  112. ... newState,
  113. }
  114. },
  115. CART_CHANGE(){
  116. const {_id} = good;
  117. return {
  118. ...state, //по аналогии с promiseReducer дописать
  119. [_id]:{good, count}
  120. }
  121. },
  122. CART_CLEAR(){
  123. return {
  124. };
  125. },
  126. }
  127. if (type in types)
  128. return types[type]()
  129. return state
  130. }
  131. const actionCartAdd = (good, count = 1) => ({type: 'CART_ADD', good, count});
  132. const actionCartRemove = (good) =>({type: 'CART_REMOVE', good});
  133. const actionCartChange = (good, count) =>({type: 'CART_CHANGE', good, count});
  134. const actionCartClear = () => ({type: 'CART_CLEAR'})
  135. const actionCartMin = (good, count = 1) =>({type: 'CART_MINUS', good, count});
  136. //понаписывать action
  137. //прикрутить к товару кнопку которая делает store.dispatch(actionCartAdd(good))
  138. //стартовое состояние может быть с токеном
  139. function combineReducers(reducers){ //перебрать все редьюсеры
  140. return (state={}, action) => { //запустить каждый их них
  141. const newState = {} //передать при этом в него ЕГО ВЕТВЬ общего state, и action как есть
  142. for (const [reducerName, reducer] of Object.entries(reducers)){ //получить newSubState
  143. let newSubState = reducer(state[reducerName], action); //если newSubState отличается от входящего, то записать newSubState в newState
  144. if (newSubState !== state[reducerName]) { //после цикла, если newState не пуст, то вернуть {...state, ...newState}
  145. newState[reducerName] = newSubState; //иначе вернуть state
  146. }
  147. } //{promise: {}, auth: {}}
  148. if (Object.keys(newState).length !== 0) {
  149. return {
  150. ... state,
  151. ... newState,
  152. }
  153. }
  154. return state;
  155. }
  156. }
  157. const combinedReducer = combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer});
  158. const store = createStore(combinedReducer);
  159. console.log(store.getState())
  160. const actionPending = name => ({type: 'PROMISE', status: 'PENDING', name})
  161. const actionResolved = (name, payload) => ({type: 'PROMISE', status: 'RESOLVED', name, payload})
  162. const actionRejected = (name, error) => ({type: 'PROMISE', status: 'REJECTED', name, error})
  163. const actionPromise = (name, promise) =>
  164. async (dispatch) => {
  165. dispatch(actionPending(name))
  166. try{
  167. let payload = await promise
  168. dispatch(actionResolved(name, payload))
  169. return payload;
  170. }
  171. catch(error){
  172. dispatch(actionRejected(name, error))
  173. }
  174. }
  175. const getGQL = url =>
  176. (query, variables = {}) =>
  177. fetch(url, {
  178. //метод
  179. method: 'POST',
  180. headers: {
  181. //заголовок content-type
  182. "Content-Type": "application/json",
  183. ...(localStorage.authToken ? {"Authorization": "Bearer " + localStorage.authToken} :
  184. {})
  185. },
  186. //body с ключами query и variables
  187. body: JSON.stringify({query, variables})
  188. })
  189. .then(res => res.json())
  190. .then(data => {
  191. if (data.errors && !data.data)
  192. throw new Error(JSON.stringify(data.errors))
  193. return data.data[Object.keys(data.data)[0]]
  194. })
  195. const backURL = 'http://shop-roles.asmer.fs.a-level.com.ua'
  196. const gql = getGQL(`${backURL}/graphql`)
  197. //store.dispatch(actionPromise('delay1000', delay(1000)))//{promise: {delay1000: '''}, auth: {}}
  198. //store.dispatch(actionAuthLogin(token))//{promise: {delay1000: '''}, auth: {token .....}}
  199. //
  200. //+ ПЕРЕДЕЛАТЬ ОТОБРАЖЕНИЕ с поправкой на то, что теперь промисы не в корне state а в state.promise
  201. const actionLogin = (login, password) =>
  202. actionPromise('login', gql(`query authorize ($login:String, $password:String){
  203. login(login:$login, password:$password)}`
  204. ,
  205. {
  206. "login": `${login}`,
  207. "password":`${password}`
  208. }
  209. )
  210. )
  211. const actionFullLogin = (login, password) =>
  212. async dispatch => {
  213. let token = await dispatch(actionLogin(login, password));
  214. if (token){
  215. dispatch(actionAuthLogin(token))
  216. }
  217. }
  218. const actionRegister = (login, password) => //const actionRegister //actionPromise
  219. actionPromise('register', gql(`mutation register($user:UserInput){
  220. UserUpsert(user:$user){
  221. _id login
  222. }
  223. }`, {"user":{
  224. "login": `${login}`,
  225. "password": `${password}`
  226. }
  227. }
  228. )
  229. )
  230. const actionFullRegister = (login, password) => //const actionFullRegister = (login, password) => //actionRegister + actionFullLogin
  231. async dispatch => {
  232. await dispatch(actionAuthLogout()); //+ интерфейс к этому - форму логина, регистрации, может повесить это на #/login #/register
  233. let reg = await dispatch(actionRegister(login, password));
  234. if(reg){
  235. dispatch(actionFullLogin(login, password))
  236. } //+ #/orders показывает ваши бывшие заказы:
  237. } //сделать actionMyOrders
  238. //проверить:
  239. //поделать store.dispatch с разными action. Скопипастить токен
  240. //проверить перезагрузку страницы.
  241. //const store = createStore(promiseReducer)
  242. store.subscribe(() => console.log(store.getState()))
  243. const actionRootCats = () =>
  244. actionPromise('rootCats', gql(`query {
  245. CategoryFind(query: "[{\\"parent\\":null}]"){
  246. _id name
  247. }
  248. }`))
  249. const actionCatById = (_id) => //добавить подкатегории
  250. actionPromise('catById', gql(`query catById($q: String){
  251. CategoryFindOne(query:$q){
  252. name subCategories {
  253. _id
  254. name
  255. goods {
  256. _id name description price images{
  257. url
  258. }
  259. }
  260. }goods {
  261. _id name price images {
  262. url
  263. }
  264. }
  265. }
  266. }`, {"q": JSON.stringify([{_id}])}))
  267. // store.dispatch(actionRootCats())
  268. const actionGoodById = (_id) =>
  269. actionPromise('goodById', gql(`query goodById($goodId:String){
  270. GoodFindOne(query:$goodId){
  271. _id name description price images{
  272. _id text url
  273. }
  274. }
  275. }`, {"goodId": JSON.stringify([{_id}])}))
  276. const actionOrder = () =>
  277. async (dispatch, getState) => {
  278. let {cart} = store.getState()
  279. //магия по созданию структуры вида
  280. //let orderGoods = [{good: {_id}, count}, {good: {_id}, count} .......]
  281. //из структуры вида
  282. //{_id1: {good, count},
  283. //_id2: {good, count}}
  284. const orderGoods = Object.entries(cart).map(([_id, {good, count}]) => ({good: {_id}, count}))
  285. await dispatch(actionPromise('order', gql(`
  286. mutation newOrder($order:OrderInput){
  287. OrderUpsert(order:$order)
  288. { _id total }
  289. }
  290. `, {order: {orderGoods}})))
  291. }
  292. //сделать actionMyOrders
  293. const actionMyOrders = () =>
  294. async (dispatch, getState) => {
  295. await dispatch(actionPromise('myOrders', gql(`query OrderFind{
  296. OrderFind(query:"[{}]"){
  297. orderGoods {price, count, good{name}}
  298. }
  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. window.onhashchange = () => {
  314. const [, route, _id] = location.hash.split('/');
  315. const routes = {
  316. order(){
  317. store.dispatch(actionMyOrders())
  318. },
  319. category(){
  320. store.dispatch(actionCatById(_id))
  321. },
  322. good(){
  323. store.dispatch(actionGoodById(_id));//задиспатчить actionGoodById
  324. console.log('ТОВАРОСТРАНИЦА')
  325. },
  326. cart(){
  327. store.dispatch(actionCatById(_id))
  328. },
  329. login(){
  330. document.getElementById('main').innerHTML = ""; //отрисовка тут
  331. const formRegistration = document.createElement('form'); //по кнопке - store.dispatch(actionFullLogin(login, password))
  332. const inputLogin = document.createElement('input');
  333. inputLogin.placeholder = "Login";
  334. inputLogin.style.display="block";
  335. const inputPassword = document.createElement('input');
  336. inputPassword.placeholder = "Password";
  337. inputPassword.style.display="block";
  338. inputPassword.style.marginTop = "10px";
  339. const buttonSignIn = document.createElement('button');
  340. buttonSignIn.innerText = "Sign in";
  341. buttonSignIn.type = 'button';
  342. buttonSignIn.style.marginTop = "10px";
  343. const buttonReset = document.createElement('button');
  344. buttonReset.type ="reset";
  345. buttonReset.innerText =" Reset";
  346. buttonReset.style.marginTop = "10px";
  347. buttonReset.style.marginLeft = "10px";
  348. main.append(formRegistration);
  349. formRegistration.append(inputLogin);
  350. formRegistration.append(inputPassword);
  351. formRegistration.append(buttonSignIn);
  352. formRegistration.append(buttonReset);
  353. buttonSignIn.addEventListener('click', async function enterStore (event) {
  354. await store.dispatch(actionFullLogin(inputLogin.value, inputPassword.value));
  355. const result = store.getState().promise.login.payload;
  356. if(result){
  357. document.getElementById('main').innerHTML = "";
  358. store.dispatch(actionRootCats());
  359. }
  360. })
  361. },
  362. register(){
  363. document.getElementById('main').innerHTML = ""; //отрисовка тут
  364. const formRegistration = document.createElement('form'); //по кнопке - store.dispatch(actionFullLogin(login, password))
  365. const inputLogin = document.createElement('input');
  366. inputLogin.placeholder = "Login";
  367. inputLogin.style.display="block";
  368. const inputPassword = document.createElement('input');
  369. inputPassword.placeholder = "Password";
  370. inputPassword.style.display="block";
  371. inputPassword.style.marginTop = "10px";
  372. const buttonSignIn = document.createElement('button');
  373. buttonSignIn.innerText = "Sign up";
  374. buttonSignIn.type = 'button';
  375. buttonSignIn.style.marginTop = "10px";
  376. const buttonReset = document.createElement('button');
  377. buttonReset.type ="reset";
  378. buttonReset.innerText =" Reset";
  379. buttonReset.style.marginTop = "10px";
  380. buttonReset.style.marginLeft = "10px";
  381. main.append(formRegistration);
  382. formRegistration.append(inputLogin);
  383. formRegistration.append(inputPassword);
  384. formRegistration.append(buttonSignIn);
  385. formRegistration.append(buttonReset);
  386. buttonSignIn.addEventListener('click', async function enterStore (event) {
  387. await store.dispatch(actionFullRegister(inputLogin.value, inputPassword.value));
  388. console.log('store.getState().promise', store.getState().promise)
  389. const result = store.getState().promise.register.payload; console.log("result",result)
  390. if(result){
  391. document.getElementById('main').innerHTML = "";
  392. store.dispatch(actionRootCats());
  393. }
  394. })
  395. },
  396. }
  397. if (route in routes)
  398. routes[route]()
  399. }
  400. window.onhashchange()
  401. store.subscribe(() => {
  402. const {catById} = store.getState().promise
  403. const [,route, _id] = location.hash.split('/')
  404. if (catById?.payload && route === 'category'){
  405. const {name, subCategories} = catById.payload;
  406. let str = '';
  407. if (subCategories){
  408. for(let subCateg of subCategories){
  409. str += `<h4><a href="#/category/${subCateg._id}"> ${subCateg.name}</a></h4>`;
  410. }
  411. }
  412. main.innerHTML = `<h1>${name}</h1> ${str} ` //ТУТ ДОЛЖНЫ БЫТЬ ПОДКАТЕГОРИИ
  413. for (const good of catById.payload.goods){ //console.log('good', good)
  414. const {_id, name, price, images} = good;
  415. const card = document.createElement('div')
  416. card.innerHTML = `<h2>${name}</h2>
  417. <img src="${backURL}/${images[0].url}" />
  418. <strong>${price}</strong>
  419. <a href="#/good/${_id}">${name}</a>
  420. `
  421. main.append(card);
  422. const buttonBuy = document.createElement('button'); //создать кнопку 'добавить в корзину' через createElement
  423. buttonBuy.innerText = "buy";
  424. card.append(buttonBuy); //card.append(кнопка)
  425. buttonBuy.onclick = () => store.dispatch(actionCartAdd(good)); //на onclick повестить обработчик который store.dispatch(actionCartAdd(good))
  426. }
  427. }
  428. })
  429. store.subscribe(() => {
  430. const {goodById} = store.getState().promise;
  431. if(goodById) {
  432. const [, , _id] = location.hash.split('/'); //ТУТ ДОЛЖНА БЫТЬ ПРОВЕРКА НА НАЛИЧИЕ goodById в редакс
  433. if(goodById?.payload && location.hash == `#/good/${_id}`){ //и проверка на то, что сейчас в адресной строке адрес ВИДА #/good/АЙДИ
  434. document.getElementById('main').innerHTML = "";
  435. const {name, description, price, images} = goodById.payload;
  436. const card = document.createElement('div'); //в таком случае очищаем main и рисуем информацию про товар с подробностями
  437. card.innerHTML = `<h2>${name}</h2>
  438. <img src="${backURL}/${images[0].url}" />
  439. <strong>${price} $</strong>
  440. <p>${description}</p>`
  441. main.append(card);
  442. const buttonBuy = document.createElement('button');
  443. buttonBuy.innerText = "buy";
  444. card.append(buttonBuy);
  445. buttonBuy.onclick = () => store.dispatch(actionCartAdd(goodById.payload));
  446. }
  447. }
  448. })
  449. store.subscribe( () => {
  450. const data = store.getState().cart;
  451. const divTotalCard = document.createElement('div');
  452. if( (Object.keys(data).length !== 0) && location.hash == `#/cart`){ console.log('data', data)
  453. document.getElementById('main').innerHTML = "";
  454. main.append(divTotalCard);
  455. const arrKeys = Object.keys(data); console.log('arrKeys', arrKeys); console.log('data[arrKeys[i]]', data[arrKeys[0]])
  456. let totalAmount = 0;
  457. let countTotal = 0;
  458. for (let i = 0; i < arrKeys.length; i++) {
  459. let {good : {name, price, images: [{url}]}, count} = data[arrKeys[i]]; console.log('count', count)
  460. const divCart = document.createElement('div');
  461. divTotalCard.append(divCart);
  462. const h3 = document.createElement('h3');
  463. divCart.appendChild(h3).innerText = `${name}`;
  464. const img = document.createElement('img');
  465. img.src = `${backURL}/${url}`;
  466. divCart.append(img);
  467. const strong =document.createElement('strong');
  468. strong.innerText = `Price ${price} $`;
  469. divCart.append(strong);
  470. const span =document.createElement('span');
  471. span.innerText = `Count ${count}`;
  472. span.style.display ='block';
  473. divCart.append(span);
  474. const buttonPlus = document.createElement('button');
  475. buttonPlus.innerText = '+';
  476. divCart.append(buttonPlus);
  477. buttonPlus.onclick = () => store.dispatch(actionCartAdd(data[arrKeys[i]].good));
  478. const buttonMinus = document.createElement('button');
  479. buttonMinus.innerText = '-';
  480. buttonMinus.onclick = () => store.dispatch(actionCartMin(data[arrKeys[i]].good));
  481. divCart.append(buttonMinus);
  482. const buttonRemove = document.createElement('button');
  483. buttonRemove.innerText = 'X';
  484. buttonRemove.onclick = () => store.dispatch(actionCartRemove(data[arrKeys[i]].good));
  485. divCart.append(buttonRemove);
  486. totalAmount += price*count;
  487. countTotal += count;
  488. }
  489. const spanFooterCart = document.createElement('span');
  490. spanFooterCart.style.marginTop = '15px';
  491. spanFooterCart.style.display = 'block';
  492. main.append(spanFooterCart);
  493. spanFooterCart.innerHTML = ` Total amount ${totalAmount}$`;
  494. const buttonClear = document.createElement('button');
  495. spanFooterCart.append(buttonClear);
  496. buttonClear.innerHTML = 'CART_CLEAR';
  497. const buttonOrderGoods = document.createElement('button');
  498. spanFooterCart.append(buttonOrderGoods);
  499. buttonOrderGoods.innerText = 'ORDER_GOODS';
  500. buttonOrderGoods.style.marginLeft = '15px';
  501. buttonOrderGoods.onclick = () => {
  502. store.dispatch(actionOrder());
  503. store.dispatch(actionCartClear());
  504. }
  505. Object.assign(buttonClear, {
  506. height: 120,
  507. width: 160,
  508. marginLeft: 20,
  509. onclick: function () {
  510. store.dispatch(actionCartClear());
  511. divTotalCard.innerHTML = '';
  512. spanFooterCart.innerHTML = '';
  513. }
  514. })
  515. if (countTotal === 0) {
  516. main.innerText = 'Товаров нет';
  517. }
  518. }
  519. })
  520. store.subscribe( () => {
  521. const data = store.getState().cart;
  522. if ((Object.keys(data).length === 0) && location.hash == `#/cart`) { console.log('Object.keys(data).length',Object.keys(data).length)
  523. main.innerText = 'Товаров нет';
  524. }
  525. })
  526. authLogOut.onclick = () => {
  527. store.dispatch(actionAuthLogout());
  528. }
  529. store.subscribe( () => {
  530. const data = store.getState().promise.myOrders;
  531. if (data?.payload && location.hash == `#/order`) {
  532. main.innerHTML = '';
  533. const arrMyOrder = data.payload;
  534. const table = document.createElement('table');
  535. main.append(table);
  536. let count =0;
  537. for (let i = 0; i < arrMyOrder.length; i++) {
  538. const table = document.createElement('table');
  539. main.append(table);
  540. let countTotal = 0;
  541. let priceOrder = 0;
  542. const orderGoods = arrMyOrder[i].orderGoods;
  543. let strTable = `<tr>
  544. <td>№ заказа</td>
  545. <td>Название</td>
  546. <td>Количество</td>
  547. <td>Цена</td>
  548. </tr>`;
  549. for (let {price, count, good: { name}} of orderGoods){
  550. strTable += `<tr>
  551. <td>${i+1}</td>
  552. <td>${name}</td>
  553. <td>${count}</td>
  554. <td>${price}</td>
  555. </tr>`;
  556. countTotal += count;
  557. priceOrder += price * count;
  558. }
  559. strTable += `<tr>
  560. <td colspan="2">Всего</td>
  561. <td>${countTotal}</td>
  562. <td>${priceOrder}</td>
  563. </tr>`;
  564. table.innerHTML = strTable;
  565. }
  566. }
  567. })
  568. store.subscribe( () => {
  569. const data = store.getState().auth.payload?.sub.login;
  570. if (data) {
  571. nameUser.innerHTML = `Здравствуй ${data}`;
  572. }
  573. })