Vika 3 years ago
parent
commit
96c8cf1b5f
3 changed files with 743 additions and 0 deletions
  1. 47 0
      js18/index.html
  2. 675 0
      js18/main.js
  3. 21 0
      js18/style.css

+ 47 - 0
js18/index.html

@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+    <head>
+        <title>GQL</title>
+        <meta charset='utf8' />
+        <style>
+            #mainContainer {
+                display: flex;
+            }
+            #aside {
+                width: 30%;
+            }
+            #aside > a{
+                display: block;
+            }
+        </style>
+        <link rel="stylesheet" href="style.css">
+    </head>
+    <body>
+        <header>
+            <p id="nameUser"></p>
+            <a href="#/cart" id='cartion'>Корзина</a>
+            <a  href="#/order" id='orderGoods'>Заказы</a>
+            <a href="">
+                <button id ='authLogOut'>Выход</button>
+            </a>    
+        </header>
+        <div id='mainContainer'>
+            <aside id='aside'>
+                Категории
+            </aside>
+            <main id='main'>
+                <div id="wrapperLogin">
+                    <a href="#/login">
+                        <button>Login</button>
+                    </a>
+                    <a href="#/register"> 
+                        <button>Registration</button>
+                    </a>
+                </div>
+                <div id="wrapperSubCategory"></div>
+                <!-- Контент -->
+            </main>
+        </div>
+        <script src='main.js'></script>
+    </body>
+</html>

+ 675 - 0
js18/main.js

@@ -0,0 +1,675 @@
+//debugger;
+
+function createStore(reducer){
+  let state       = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
+  let cbs         = []                     //массив подписчиков
+  
+  const getState  = () => state            //функция, возвращающая переменную из замыкания
+  const subscribe = cb => (cbs.push(cb),   //запоминаем подписчиков в массиве
+                           () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
+                           
+  const dispatch  = action => { 
+    
+      if (typeof action === 'function'){ //если action - не объект, а функция
+          return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы
+      }
+      const newState = reducer(state, action) //пробуем запустить редьюсер
+      if (newState !== state){ //проверяем, смог ли редьюсер обработать action
+          state = newState //если смог, то обновляем state 
+          for (let cb of cbs)  cb() //и запускаем подписчиков
+      }
+  }
+  
+  return {
+      getState, //добавление функции getState в результирующий объект
+      dispatch,
+      subscribe //добавление subscribe в объект
+  }
+}
+
+function promiseReducer(state={}, {type, name, status, payload, error}){
+  // {
+  //    login: {status, payload, error}
+  //    catById: {status, payload, error}
+  // }
+  if (type === 'PROMISE'){
+      return {
+          ...state,
+          [name]:{status, payload, error}
+      }
+  }
+  return state
+}
+
+//const store = createStore(promiseReducer);
+
+function jwtDecode(token){
+  try {let base64Url = token.split('.')[1];
+  let base64 = atob(base64Url);
+  return JSON.parse(base64); 
+  } catch { (err) =>
+    console.log(err);
+  }
+                                        //раскодировать токен:
+                                          //выкусить середочку
+                                          //atob
+                                          //JSON.parse
+                                          //на любом этапе могут быть исключения
+}
+
+function authReducer(state, {type, token}){
+  
+  if (!state){
+      if(localStorage.authToken){  //проверить localStorage.authToken на наличие
+        return {
+        'type': 'AUTH_LOGIN',
+        'token':localStorage.authToken,
+        };                         //если есть - сделать так, что бы следующий if сработал
+      }  else { return state = {}}                            //если нет - вернуть {}
+  }
+  if (type === 'AUTH_LOGIN'){
+    const bigToken =  jwtDecode(token);   //взять токен из action
+    if (bigToken) {                                //попытаться его jwtDecode
+      localStorage.setItem('authToken', token);                              //если удалось, то:
+      return { 
+        token,
+        payload: bigToken,
+      }
+    }                                              //сохранить токен в localStorage
+  }                                                //вернуть объект вида {токен, payload: раскодированный токен}
+  if (type === 'AUTH_LOGOUT'){
+    localStorage.clear();                          //почистить localStorage
+    return {};                                     //вернуть пустой объект
+  }
+  return state
+}
+
+const actionAuthLogin  = token => ({type: 'AUTH_LOGIN', token})
+const actionAuthLogout = ()    => ({type: 'AUTH_LOGOUT'})
+
+//const store = createStore(authReducer)
+
+
+function cartReducer(state={}, {type, good = {}, count=1}){
+  //{
+  //  _id1: {good, count}
+  //  _id2: {good, count}
+  //}
+  const types = {
+    
+      CART_ADD(){ //как CHANGE, только если ключ раньше был, то достать из count и добавить
+        const {_id} = good;   
+                               //к count из action. Если не было, достать 0 и добавить к count из action
+        return {
+         ...state,
+         [_id]:{good, count:count +(state[_id]?.count || 0)}
+       }
+      },
+      CART_MINUS(){ //как CHANGE, только если ключ раньше был, то достать из count и добавить
+        const {_id} = good;   
+                               //к count из action. Если не было, достать 0 и добавить к count из action
+        return {
+         ...state,
+         [_id]:{good, count: (-count +(state[_id]?.count || 0)) < 1 ? 0: -count +(state[_id]?.count || 0)}
+       }
+      },
+      CART_REMOVE(){ //смочь скопировать объект и выкинуть ключ. как вариант через
+                      //деструктуризацию
+        const {_id} = good; 
+        let newState = {...state};
+        const arrKeys = Object.keys(newState);
+        for (let key of arrKeys) {
+          if(_id === key) {
+            delete newState[_id];
+          }
+        } 
+        
+        return {
+          ... newState,
+        }  
+      },
+      CART_CHANGE(){
+        const {_id} = good;
+          return {
+            ...state, //по аналогии с promiseReducer дописать
+            [_id]:{good, count}
+          }
+      },
+      CART_CLEAR(){
+      
+        return {
+          
+        };
+      },
+  }
+
+  if (type in types)
+      return types[type]()
+
+
+  return state
+}
+
+const actionCartAdd = (good, count = 1) => ({type: 'CART_ADD', good, count});
+const actionCartRemove = (good) =>({type: 'CART_REMOVE', good});
+const actionCartChange = (good, count) =>({type: 'CART_CHANGE', good, count});
+const actionCartClear = () => ({type: 'CART_CLEAR'})
+const actionCartMin = (good, count = 1) =>({type: 'CART_MINUS', good, count});
+
+
+//понаписывать action
+//прикрутить к товару кнопку которая делает store.dispatch(actionCartAdd(good))
+
+                                                                         //стартовое состояние может быть с токеном
+
+function combineReducers(reducers){                                      //перебрать все редьюсеры
+  return (state={}, action) => {                                         //запустить каждый их них
+    const newState = {}                                                  //передать при этом в него ЕГО ВЕТВЬ общего state, и action как есть   
+    for (const [reducerName, reducer] of Object.entries(reducers)){      //получить newSubState
+      let newSubState = reducer(state[reducerName], action);             //если newSubState отличается от входящего, то записать newSubState в newState
+      if (newSubState !== state[reducerName]) {                          //после цикла, если newState не пуст, то вернуть {...state, ...newState} 
+        newState[reducerName] = newSubState;                             //иначе вернуть state
+      }
+    }                                                                     //{promise: {}, auth: {}}
+
+    if (Object.keys(newState).length !== 0) {
+      return {
+        ... state,
+        ... newState,
+      }
+    }
+    return state;
+  }
+}
+const combinedReducer = combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer});
+const store = createStore(combinedReducer);
+console.log(store.getState())
+
+const actionPending  = name => ({type: 'PROMISE', status: 'PENDING', name})
+const actionResolved = (name, payload) => ({type: 'PROMISE', status: 'RESOLVED', name, payload})
+const actionRejected = (name, error) => ({type: 'PROMISE', status: 'REJECTED', name,  error})
+
+const actionPromise = (name, promise) =>
+  async (dispatch) => {
+      dispatch(actionPending(name)) 
+      try{
+          let payload = await promise
+          dispatch(actionResolved(name, payload))
+          return payload;
+      }
+      catch(error){
+          dispatch(actionRejected(name, error))
+      }
+  }
+
+  const getGQL = url =>
+  (query, variables = {}) =>
+      fetch(url, {
+      //метод
+      method: 'POST',
+      headers: {
+          //заголовок content-type
+          "Content-Type": "application/json",
+          ...(localStorage.authToken ? {"Authorization": "Bearer " + localStorage.authToken} :
+                                       {})
+      },
+      //body с ключами query и variables 
+      body: JSON.stringify({query, variables})
+      })
+      .then(res => res.json())
+      .then(data => {
+          if (data.errors && !data.data)
+              throw new Error(JSON.stringify(data.errors))
+          return data.data[Object.keys(data.data)[0]]
+      })
+
+const backURL = 'http://shop-roles.asmer.fs.a-level.com.ua'
+  
+const gql = getGQL(`${backURL}/graphql`)
+
+
+                                          //store.dispatch(actionPromise('delay1000', delay(1000)))//{promise: {delay1000: '''}, auth: {}}
+                                          //store.dispatch(actionAuthLogin(token))//{promise: {delay1000: '''}, auth: {token .....}}
+                                          //
+                                          //+ ПЕРЕДЕЛАТЬ ОТОБРАЖЕНИЕ с поправкой на то, что теперь промисы не в корне state а в state.promise
+
+const actionLogin = (login, password) =>
+    actionPromise('login', gql(`query authorize ($login:String, $password:String){
+      login(login:$login, password:$password)}`
+    , 
+      {
+        "login": `${login}`,
+        "password":`${password}`
+      }
+    )
+  )
+
+const actionFullLogin = (login, password) =>
+  async dispatch => {
+      let token = await dispatch(actionLogin(login, password)); 
+      if (token){
+          dispatch(actionAuthLogin(token))
+      }
+  }
+
+const actionRegister = (login, password) =>                  //const actionRegister //actionPromise
+  actionPromise('register', gql(`mutation register($user:UserInput){
+    UserUpsert(user:$user){
+      _id login
+    }
+  }`, {"user":{
+        "login": `${login}`,
+        "password": `${password}`
+        }
+      }
+    )
+  )
+               
+const actionFullRegister = (login, password) =>   //const actionFullRegister = (login, password) => //actionRegister + actionFullLogin
+  async dispatch => {      
+    await dispatch(actionAuthLogout());                      //+ интерфейс к этому - форму логина, регистрации, может повесить это на #/login #/register 
+    let reg = await dispatch(actionRegister(login, password)); 
+    if(reg){
+      dispatch(actionFullLogin(login, password))
+    }                                                    //+ #/orders показывает ваши бывшие заказы:
+  }                                                    //сделать actionMyOrders
+                                               
+
+  
+    //проверить:
+    //поделать store.dispatch с разными action. Скопипастить токен
+    //проверить перезагрузку страницы.
+    
+//const store = createStore(promiseReducer)
+   
+
+store.subscribe(() => console.log(store.getState()))
+
+const actionRootCats = () => 
+  actionPromise('rootCats', gql(`query {
+      CategoryFind(query: "[{\\"parent\\":null}]"){
+          _id name
+      }
+  }`))
+
+const actionCatById = (_id) =>  //добавить подкатегории
+  actionPromise('catById', gql(`query catById($q: String){
+      CategoryFindOne(query:$q){
+         name subCategories {
+          _id
+          name 
+          goods {
+            _id name description price images{
+            url
+            }
+          }
+       }goods {
+         _id name price images {
+           url
+         }
+       }
+     }
+   }`, {"q": JSON.stringify([{_id}])}))
+
+// store.dispatch(actionRootCats())
+
+const actionGoodById = (_id) =>
+   actionPromise('goodById', gql(`query goodById($goodId:String){
+    GoodFindOne(query:$goodId){
+        _id name description price images{
+        _id text url
+      }
+    }
+  }`, {"goodId": JSON.stringify([{_id}])}))
+  
+
+const actionOrder = () =>
+  async (dispatch, getState) => {
+      let {cart} = store.getState()
+                                                                          //магия по созданию структуры вида
+                                                                      //let orderGoods = [{good: {_id}, count}, {good: {_id}, count} .......]
+                                                                      //из структуры вида
+                                                                          //{_id1: {good, count},
+                                                                          //_id2: {good, count}}
+      const orderGoods = Object.entries(cart).map(([_id, {good, count}]) => ({good: {_id}, count}))
+
+      await dispatch(actionPromise('order', gql(`
+                  mutation newOrder($order:OrderInput){
+                    OrderUpsert(order:$order)
+                      { _id total 	}
+                  }
+          `, {order: {orderGoods}})))
+}
+  
+ //сделать actionMyOrders
+const actionMyOrders = () =>
+  async (dispatch, getState) => {
+    await dispatch(actionPromise('myOrders', gql(`query OrderFind{
+      OrderFind(query:"[{}]"){
+        orderGoods {price, count, good{name}}
+      }
+    }`)))
+  }
+ 
+
+store.subscribe(() => {
+  const {rootCats} = store.getState().promise
+  if (rootCats?.payload){
+      aside.innerHTML = ''
+      for (const {_id, name} of rootCats?.payload){
+          const link      = document.createElement('a')
+          link.href       = `#/category/${_id}`
+          link.innerText  = name
+          aside.append(link)
+      }
+  }
+})
+
+
+window.onhashchange = () => {
+  const [, route, _id] = location.hash.split('/'); 
+ 
+  const routes = {
+      order(){
+        store.dispatch(actionMyOrders())
+      },
+
+      category(){
+          store.dispatch(actionCatById(_id))
+      },
+      good(){ 
+          store.dispatch(actionGoodById(_id));//задиспатчить actionGoodById
+          console.log('ТОВАРОСТРАНИЦА')
+
+      },
+      cart(){
+        store.dispatch(actionCatById(_id))
+
+      },
+
+      login(){
+        document.getElementById('main').innerHTML = "";     //отрисовка тут
+        const formRegistration = document.createElement('form');           //по кнопке - store.dispatch(actionFullLogin(login, password))
+        const inputLogin = document.createElement('input');
+        inputLogin.placeholder = "Login";
+        inputLogin.style.display="block";
+
+        const inputPassword = document.createElement('input');
+        inputPassword.placeholder = "Password";
+        inputPassword.style.display="block";
+        inputPassword.style.marginTop = "10px";
+
+        const buttonSignIn = document.createElement('button');
+        buttonSignIn.innerText = "Sign in";
+        buttonSignIn.type = 'button';
+        buttonSignIn.style.marginTop = "10px";
+
+        const buttonReset = document.createElement('button');
+        buttonReset.type ="reset";
+        buttonReset.innerText =" Reset";
+        buttonReset.style.marginTop = "10px";
+        buttonReset.style.marginLeft = "10px";
+
+        main.append(formRegistration);
+        formRegistration.append(inputLogin);
+        formRegistration.append(inputPassword);
+        formRegistration.append(buttonSignIn);
+        formRegistration.append(buttonReset);
+
+        buttonSignIn.addEventListener('click', async function enterStore (event) {
+          await store.dispatch(actionFullLogin(inputLogin.value, inputPassword.value)); 
+          const result = store.getState().promise.login.payload;
+            if(result){
+              document.getElementById('main').innerHTML = ""; 
+              store.dispatch(actionRootCats());
+
+            }
+        })
+      },
+
+      register(){ 
+        document.getElementById('main').innerHTML = "";     //отрисовка тут
+        const formRegistration = document.createElement('form');           //по кнопке - store.dispatch(actionFullLogin(login, password))
+        const inputLogin = document.createElement('input');
+        inputLogin.placeholder = "Login";
+        inputLogin.style.display="block";
+
+        const inputPassword = document.createElement('input');
+        inputPassword.placeholder = "Password";
+        inputPassword.style.display="block";
+        inputPassword.style.marginTop = "10px";
+
+        const buttonSignIn = document.createElement('button');
+        buttonSignIn.innerText = "Sign up";
+        buttonSignIn.type = 'button';
+        buttonSignIn.style.marginTop = "10px";
+
+        const buttonReset = document.createElement('button');
+        buttonReset.type ="reset";
+        buttonReset.innerText =" Reset";
+        buttonReset.style.marginTop = "10px";
+        buttonReset.style.marginLeft = "10px";
+
+        main.append(formRegistration);
+        formRegistration.append(inputLogin);
+        formRegistration.append(inputPassword);
+        formRegistration.append(buttonSignIn);
+        formRegistration.append(buttonReset);
+
+        buttonSignIn.addEventListener('click', async function enterStore (event) {
+          await store.dispatch(actionFullRegister(inputLogin.value, inputPassword.value)); 
+          console.log('store.getState().promise', store.getState().promise)
+          const result = store.getState().promise.register.payload; console.log("result",result)
+            if(result){
+              document.getElementById('main').innerHTML = ""; 
+              store.dispatch(actionRootCats());
+            }
+        })
+      },
+  }
+
+  if (route in routes)
+      routes[route]()
+  
+}
+window.onhashchange()
+
+store.subscribe(() => {
+  const {catById} = store.getState().promise
+  const [,route, _id] = location.hash.split('/')
+  if (catById?.payload && route === 'category'){
+      const {name, subCategories} = catById.payload;
+      let str = '';
+      if (subCategories){
+        for(let subCateg of subCategories){
+            str += `<h4><a href="#/category/${subCateg._id}"> ${subCateg.name}</a></h4>`;
+        }
+      } 
+      main.innerHTML = `<h1>${name}</h1> ${str} ` //ТУТ ДОЛЖНЫ БЫТЬ ПОДКАТЕГОРИИ
+      for (const good of catById.payload.goods){  //console.log('good', good)
+          const {_id, name, price, images} = good;
+          const card      = document.createElement('div')
+          card.innerHTML = `<h2>${name}</h2>
+                            <img src="${backURL}/${images[0].url}" />
+                            <strong>${price}</strong>
+                            <a href="#/good/${_id}">${name}</a>
+                                
+                              `
+          main.append(card);
+          const buttonBuy = document.createElement('button'); //создать кнопку 'добавить в корзину' через createElement
+          buttonBuy.innerText = "buy";
+          card.append(buttonBuy);  //card.append(кнопка)   
+          buttonBuy.onclick = () => store.dispatch(actionCartAdd(good));  //на onclick повестить обработчик который store.dispatch(actionCartAdd(good))
+      }
+  }
+})
+
+
+   
+                       
+store.subscribe(() => { 
+    const {goodById} = store.getState().promise;  
+    if(goodById) {
+        const [, , _id] = location.hash.split('/');   //ТУТ ДОЛЖНА БЫТЬ ПРОВЕРКА НА НАЛИЧИЕ goodById в редакс
+        if(goodById?.payload && location.hash == `#/good/${_id}`){               //и проверка на то, что сейчас в адресной строке адрес ВИДА #/good/АЙДИ
+            document.getElementById('main').innerHTML = "";  
+            const {name, description, price, images} = goodById.payload;  
+            const card = document.createElement('div');  //в таком случае очищаем main и рисуем информацию про товар с подробностями
+            card.innerHTML = `<h2>${name}</h2>
+                                <img src="${backURL}/${images[0].url}" />
+                                <strong>${price} $</strong>
+                                <p>${description}</p>`
+                                         
+        main.append(card); 
+        const buttonBuy = document.createElement('button');
+        buttonBuy.innerText = "buy";
+        card.append(buttonBuy);
+        buttonBuy.onclick = () => store.dispatch(actionCartAdd(goodById.payload));               
+    }
+    }
+})       
+
+
+store.subscribe( () => {
+  const data =  store.getState().cart; 
+  const divTotalCard = document.createElement('div');
+  
+  if( (Object.keys(data).length !== 0)  && location.hash == `#/cart`){ console.log('data', data)
+    document.getElementById('main').innerHTML = ""; 
+    main.append(divTotalCard);
+    const arrKeys = Object.keys(data); console.log('arrKeys', arrKeys); console.log('data[arrKeys[i]]', data[arrKeys[0]])
+    let totalAmount = 0;
+    let countTotal = 0;
+    for (let i = 0; i < arrKeys.length; i++) {
+      let {good : {name, price, images: [{url}]}, count} = data[arrKeys[i]]; console.log('count', count)
+      
+      const divCart = document.createElement('div');
+      divTotalCard.append(divCart);
+      const h3 = document.createElement('h3');
+      divCart.appendChild(h3).innerText = `${name}`;
+      const img = document.createElement('img');
+      img.src = `${backURL}/${url}`;
+      divCart.append(img);
+      const strong =document.createElement('strong');
+      strong.innerText = `Price ${price} $`;
+      divCart.append(strong);
+      const span =document.createElement('span');
+      span.innerText = `Count ${count}`;
+      span.style.display ='block';
+      divCart.append(span);
+      const  buttonPlus = document.createElement('button');
+      buttonPlus.innerText = '+';
+      divCart.append(buttonPlus);
+      buttonPlus.onclick = () => store.dispatch(actionCartAdd(data[arrKeys[i]].good));  
+      const  buttonMinus = document.createElement('button');
+      buttonMinus.innerText = '-';
+      buttonMinus.onclick = () =>  store.dispatch(actionCartMin(data[arrKeys[i]].good));
+      divCart.append(buttonMinus);
+      const buttonRemove = document.createElement('button');
+      buttonRemove.innerText = 'X';
+      buttonRemove.onclick = () => store.dispatch(actionCartRemove(data[arrKeys[i]].good));
+      divCart.append(buttonRemove);
+      totalAmount += price*count;
+      countTotal += count;
+    }
+
+
+    const spanFooterCart = document.createElement('span');
+    spanFooterCart.style.marginTop = '15px';
+    spanFooterCart.style.display = 'block';
+    main.append(spanFooterCart);
+    spanFooterCart.innerHTML = `   Total amount  ${totalAmount}$`;
+    const buttonClear = document.createElement('button');
+    spanFooterCart.append(buttonClear);
+    buttonClear.innerHTML = 'CART_CLEAR';
+    const buttonOrderGoods = document.createElement('button'); 
+    spanFooterCart.append(buttonOrderGoods);
+    buttonOrderGoods.innerText = 'ORDER_GOODS';
+    buttonOrderGoods.style.marginLeft = '15px';
+    buttonOrderGoods.onclick = () => { 
+      store.dispatch(actionOrder()); 
+      store.dispatch(actionCartClear());
+      
+    }
+    Object.assign(buttonClear, {
+      height: 120, 
+      width: 160, 
+      marginLeft: 20,
+      onclick: function () {
+        store.dispatch(actionCartClear());
+        divTotalCard.innerHTML = '';
+        spanFooterCart.innerHTML = '';
+      }
+    })
+    if (countTotal === 0) {
+      main.innerText = 'Товаров нет';
+    }
+  } 
+
+}) 
+
+
+store.subscribe( () => {
+  const data =  store.getState().cart; 
+  if ((Object.keys(data).length === 0) && location.hash == `#/cart`) { console.log('Object.keys(data).length',Object.keys(data).length)
+    main.innerText = 'Товаров нет';
+  }
+})
+
+authLogOut.onclick =  () => {
+  store.dispatch(actionAuthLogout());
+
+}
+
+store.subscribe( () => {
+  
+  const data =  store.getState().promise.myOrders; 
+  
+  if (data?.payload && location.hash == `#/order`) {
+    main.innerHTML = '';
+    const arrMyOrder = data.payload;
+    const table = document.createElement('table');
+    main.append(table);
+    let count =0;
+
+    for (let i = 0; i < arrMyOrder.length; i++) {
+      const table = document.createElement('table');
+      main.append(table);
+      let countTotal = 0;
+      let priceOrder = 0;
+      const orderGoods = arrMyOrder[i].orderGoods;
+     let strTable = `<tr> 
+                    <td>№ заказа</td>
+                    <td>Название</td>
+                    <td>Количество</td>
+                    <td>Цена</td>
+                  </tr>`;
+      
+      for (let {price, count, good: { name}} of orderGoods){
+     
+        strTable += `<tr> 
+                      <td>${i+1}</td>
+                      <td>${name}</td>
+                      <td>${count}</td>
+                      <td>${price}</td>
+                    </tr>`;
+        countTotal += count;  
+        priceOrder += price * count;          
+      }  
+      strTable += `<tr>
+      <td colspan="2">Всего</td>
+      <td>${countTotal}</td>
+      <td>${priceOrder}</td>
+      </tr>`;
+      table.innerHTML = strTable;
+    }
+  }
+})
+
+store.subscribe( () => {
+  const data =  store.getState().auth.payload?.sub.login;
+
+  if (data) {
+    nameUser.innerHTML = `Здравствуй ${data}`;
+  }
+})

+ 21 - 0
js18/style.css

@@ -0,0 +1,21 @@
+header {
+  display: flex;
+  justify-content: flex-end;
+}
+#cartion {
+margin-right: 35px;
+}
+ #orderGoods{
+  margin-right: 20px;
+}
+#nameUser {
+  margin-right: 55px;
+  color: #4c53d4;
+}
+table {
+  width: 100%; 
+  margin-top: 20px;
+ }
+ td {
+  border: 2px solid #4c53d4; 
+ }