anton123 2 năm trước cách đây
mục cha
commit
114b98e923

+ 14 - 10
Async, Await/Chat.html

@@ -11,7 +11,7 @@
 	<main id="main"></main>
 <script >
 
-	const jsonPost = (url, data) => {
+	function jsonPost (url, data){
 		return fetch(url, {method: 'POST',body: JSON.stringify(data)})
 		.then(result => {
 			if (result.status === 200) {
@@ -21,15 +21,14 @@
 			}
 		}).catch(error => {console.log(error)})
 	}
-	async function sendMessage(nick, message){
+	function sendMessage(nick, message){
 		jsonPost("http://students.a-level.com.ua:10012", {func: 'addMessage', nick: nick, message: message})
-		.then(data => data)
 	}
 	
 
 	async function getMessages(){
-		jsonPost("http://students.a-level.com.ua:10012",{func: "getMessages", messageId: 0})
-		.then(data => {
+		let data= await jsonPost("http://students.a-level.com.ua:10012",{func: "getMessages", messageId: 0})
+		function update (data) {
 		let dataRevers = data.data.reverse()
 		document.getElementById("main").replaceChildren()
 			for(let el of dataRevers){
@@ -37,18 +36,23 @@
 				div.innerText= `${el.nick}: ${el.message}`
 				document.getElementById('main').append(div)
 			}	
-		})
+		}
+		update(data)
 	}
 	getMessages()
 
-	async function sendAndCheck(){
+	function sendAndCheck(){
 		sendMessage(document.getElementById("nick").value, document.getElementById("message").value)
-		await getMessages()
+		getMessages()
 	}
 
+	const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
 	
-	function checkLoop(){
-		setInterval(()=>getMessages(),2000)
+	async function checkLoop(){
+		while(true){
+			await delay(2000)
+			getMessages()
+		}
 	}
 	checkLoop()
 </script>

+ 4 - 4
Async, Await/SWAPI Links.js

@@ -8,9 +8,9 @@
                 let i = 0
                 for(let elArr of obj[el]){
                     async function btn (link){  
-                        return fetch(link)
-                        .then(res => res.json())
-                        .then(luke => obj[el][i]=luke)   
+                        let res = await fetch(link)
+                        let data = await res.json()
+                        return obj[el][i]=data   
                     }
                     if(elArr.indexOf("https://swapi.dev")!== -1){
                        await btn(elArr)
@@ -24,7 +24,7 @@
                 .then(luke => obj[el]=luke)
             }
         }
-        console.log(obj)
+         console.log(obj)
     }
     fetch('https://swapi.dev/api/people/20')
     .then(res => res.json())

+ 38 - 0
REST, GraphQL и JWT/gql.js

@@ -0,0 +1,38 @@
+//gql
+{
+    async function gql(url, query, options) {
+        let settings = {
+            method: "POST",
+            headers:{
+                "Content-Type": "application/json",
+                "Accept": "application/json"
+            },
+            body: JSON.stringify({query: query, variables: options})
+        }
+            const res = await fetch(url, settings)
+            return await res.json()
+    }
+    
+    
+    async function login() {
+        const catQuery = `query cats($q: String){
+                                            CategoryFind(query: $q){
+                                                _id name
+                                            }
+                                        }`
+        const cats = await gql("http://shop-roles.node.ed.asmer.org.ua/graphql",  catQuery,  {q: "[{}]"})
+        console.log(cats) //список категорий с _id name и всем таким прочим
+        
+        
+        const loginQuery = `query login($login:String, $password:String){
+                                    login(login:$login, password:$password)
+                            }`
+        
+        const token = await gql("http://shop-roles.node.ed.asmer.org.ua/graphql", loginQuery ,{login: "test457", password: "123123"})
+        console.log(token)
+    }
+    login() 
+}
+
+
+

+ 26 - 0
REST, GraphQL и JWT/jwtDecode.js

@@ -0,0 +1,26 @@
+//jwtDecode
+{
+    function jwtDecode(token){
+        try {
+            return console.log(JSON.parse(atob(token.split('.').slice(1,2))))
+        }
+        catch {
+            return undefined;
+        }
+        
+    }
+    
+    const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOnsiaWQiOiI2MzIyMDVhZWI3NGUxZjVmMmVjMWEzMjAiLCJsb2dpbiI6InRlc3Q0NTciLCJhY2wiOlsiNjMyMjA1YWViNzRlMWY1ZjJlYzFhMzIwIiwidXNlciJdfSwiaWF0IjoxNjY4MjcyMTYzfQ.rxV1ki9G6LjT2IPWcqkMeTi_1K9sb3Si8vLB6UDAGdw"
+    console.log(jwtDecode(token)) 
+
+    try {
+        console.log(jwtDecode())         //undefined
+        console.log(jwtDecode("дичь"))   //undefined
+        console.log(jwtDecode("ey.ey.ey"))   //undefined
+        
+        console.log('до сюда доработало, а значит jwtDecode не матерился в консоль красным цветом')
+    }
+    finally{
+        console.log('ДЗ, видимо, окончено')
+    }
+}

+ 53 - 0
REST, GraphQL и JWT/Светофор для машин.html

@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Document</title>
+</head>
+<body>
+    <main id="main" style="width: max-content;">Светофор для машин</main>
+
+    <script>
+        const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
+
+        let divGreen = document.createElement('div')
+        let divYelow = document.createElement('div')
+        let divRed = document.createElement('div')
+
+        async function trafficLight(domElement,timeGreen,timeYelow,timeRed){
+            divGreen.style="background-color: rgb(1, 51, 1) ;"
+            divYelow.style="background-color: rgb(105, 111, 1) ;"
+            divRed.style="background-color: rgb(97, 2, 2) ;"
+            let greenOn =()=> divGreen.style="background-color: rgb(0, 216, 0) ;"
+            let greenOf =()=> divGreen.style="background-color: rgb(12, 105, 12) ;"
+            let yelowOf =()=> divYelow.style="background-color: rgb(105, 111, 1) ;"
+            let yelowOn =()=> divYelow.style="background-color: rgb(202, 216, 0) ;"
+            let redOn =()=> divRed.style="background-color: rgb(216, 0, 0) ;"
+            let redOf =()=> divRed.style="background-color: rgb(139, 7, 7);"
+            divGreen.innerText='Зеленый'
+            divYelow.innerText='Желтый'
+            divRed.innerText='Красный'
+            domElement.append(divGreen,divYelow,divRed)
+            while (true){
+                yelowOf()
+                redOn()
+                await delay(timeRed)
+                redOf()
+                yelowOn()
+                await delay(timeYelow)
+                yelowOf()
+                greenOn()
+                await delay(timeGreen)
+                greenOf()
+                yelowOn()
+                await delay(timeYelow)
+            }
+        }
+        
+        trafficLight(document.getElementById('main'),5000,1000,5000)
+
+    </script>
+</body>
+</html>

+ 69 - 0
REST, GraphQL и JWT/Светофор для пешеходов.html

@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Светофор для пешеходов</title>
+</head>
+<body>
+    
+    <main id="main" style="width: max-content;">Светофор для пешеходов</main>
+    
+    <script>
+        const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
+
+        let divGreen = document.createElement('div')
+        let divRed = document.createElement('div')
+        divGreen.innerText='Зеленый'
+        divRed.innerText='Красный'
+        let greenOn =()=> divGreen.style="background-color: rgb(0, 216, 0) ;"
+        let greenOf =()=> divGreen.style="background-color: rgb(12, 105, 12) ;"
+        let redOn =()=> divRed.style="background-color: rgb(216, 0, 0) ;"
+        let redOf =()=> divRed.style="background-color: rgb(139, 7, 7);"
+        
+        let animation = true
+        
+        async function trafficLight(timeGreen,timeRed){
+            greenOf()
+            redOf()
+            
+            while (animation){
+                redOf()
+                greenOn()
+                await delay(timeGreen)
+                greenOf()
+                redOn()
+                await delay(timeRed)
+            }
+        }
+        
+
+        trafficLight(5000,6000)
+
+        let knopka = document.createElement('button')
+        knopka.innerText='Кнопка'
+        document.body.append(knopka)
+        function domEventPromise(element, eventName) {
+            function executor(resolve) {
+                async function handler(event) {
+                    animation=false
+                    redOf()
+                    greenOn()
+                    await delay(5000)
+                    greenOf()
+                    animation=true
+                    trafficLight(5000,6000)
+                    resolve(event);
+                }
+                element.addEventListener(eventName, handler);
+            }
+            return new Promise(executor);
+        }
+        domEventPromise(knopka, 'click').then(e => e);
+
+        document.getElementById('main').append(divGreen,divRed)
+
+    </script>
+</body>
+</html>

+ 36 - 0
Модуль js/Module.html

@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>GQL</title>
+        
+        <style>
+            #mainContainer {
+                display: flex;
+            }
+            #aside {
+                width: 30%;
+            }
+            #aside > a{
+                display: block;
+            }
+            header {
+                min-height: 100px;
+                background-color: #AAA;
+            }
+        </style>
+    </head>
+    <body>
+        <header>
+            
+            <div id="cartIcon" ></div>
+            <div id="reg"><a href="#/register">Регистрация</a></div>
+            <div id="login"><a href="#/login">Логин</a></div>
+        </header>
+        <div id="mainContainer">
+            <aside id="aside">
+            </aside>
+            <main id="main"><h1>Выберите категорию</h1></main>
+        </div>
+        <script src="index.js"></script>
+    
+
+</body></html>

+ 636 - 0
Модуль js/index.js

@@ -0,0 +1,636 @@
+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(state) //и запускаем подписчиков
+        }
+    }
+    
+    return {
+        getState, //добавление функции getState в результирующий объект
+        dispatch,
+        subscribe //добавление subscribe в объект
+    }
+}
+
+function combineReducers(reducers){
+	function totalReducer(state={}, action){
+		const newTotalState = {}
+		for (const [reducerName, reducer] of Object.entries(reducers)){
+			const newSubState = reducer(state[reducerName], action)
+			if (newSubState !== state[reducerName]){
+				newTotalState[reducerName] = newSubState
+			}
+		}
+		if (Object.keys(newTotalState).length){
+			return {...state, ...newTotalState}
+		}
+
+		return state
+	}
+
+	return totalReducer
+}
+
+
+
+
+function jwtDecode(token){
+	try {
+		return JSON.parse(atob(token.split('.')[1]))
+	}
+	catch(e){
+	}
+}
+
+function localStoredReducer(reducer, localStorageKey){
+	function wrapper(state, action){
+		if (state === undefined){
+			try {
+				return JSON.parse(localStorage[localStorageKey]) 
+			}
+			catch(e){ } 
+		}
+		const newState = reducer(state, action)
+		localStorage.setItem(localStorageKey, JSON.stringify(newState)) 
+		return newState
+	}
+	return wrapper
+}
+
+const reducers = {
+    auth: authReducer,
+	cart: localStoredReducer(cartReducer, 'cart'), 
+	promise: localStoredReducer(promiseReducer, 'promise'),
+}
+
+
+function promiseReducer(state={}, {type, status, payload, error,namePromise}){
+    if (type === 'PROMISE'){
+        return {
+        ...state,
+          [namePromise] : {status, payload, error}
+        }
+      }
+      return state
+}
+
+const actionPending   = (namePromise)      => ({type: 'PROMISE', status: 'PENDING',namePromise})
+const actionFulfilled = (namePromise,payload) => ({type: 'PROMISE', status: 'FULFILLED',namePromise, payload})
+const actionRejected  = (namePromise,error)  => ({type: 'PROMISE', status: 'REJECTED', namePromise, error})
+
+
+const actionPromise = (namePromise,promise) =>
+    async dispatch => { 
+        dispatch(actionPending(namePromise)) //сигнализируем redux, что промис начался
+        try{
+            const payload = await promise //ожидаем промиса
+            dispatch(actionFulfilled(namePromise,payload)) //сигнализируем redux, что промис успешно выполнен
+            return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
+        }
+        catch (error){
+            dispatch(actionRejected(namePromise,error)) //в случае ошибки - сигнализируем redux, что промис несложился
+        }
+    }
+
+
+
+
+const store = createStore(combineReducers(reducers)) //не забудьте combineReducers если он у вас уже есть
+store.subscribe(() => console.log(store.getState()))
+
+
+function authReducer(state={}, {type, token}){
+	if (type === 'AUTH_LOGIN'){ 
+		const payload = jwtDecode(token)
+		if (payload){
+			return {
+				token,
+				payload
+			}
+		}
+	}
+	if (type === 'AUTH_LOGOUT'){ 
+		return {}
+	}
+	return state
+}
+
+const actionAuthLogout = () =>
+	() => {
+		store.dispatch({type: 'AUTH_LOGOUT'});
+		localStorage.removeItem('authToken');
+	}
+
+const actionAuthLogin = (token) =>
+	() => {
+		const oldState = store.getState()
+		store.dispatch({type: 'AUTH_LOGIN', token})
+		const newState = store.getState()
+		if (oldState !== newState)
+			localStorage.setItem('authToken', token)
+	}
+
+
+function cartReducer(state={}, {type, count, good}){
+    if(type==='CART_ADD'){
+        if(typeof state[good._id]==='object'){
+            let newCount = state[good._id].count+count
+            return{
+                ...state,
+                [good._id]:{good:good, count:newCount}
+            }
+        }
+        else{return{
+            ...state,
+            [good._id]:{good:good, count}
+            }
+        }
+    }
+    if(type==='CART_SET'){
+        return{
+            ...state,
+            [good._id]:{good:good, count}
+        }
+    }
+    if(type==='CART_SUB'){
+        let newCount = state[good._id].count-count
+        if(newCount>0){
+            return{
+                ...state,
+                [good._id]:{good:good, count:newCount}
+            }
+        }else{
+            delete state[good._id]
+        }
+    }
+    if(type==='CART_DEL'){
+        const {[good._id]: x,...newState} = state
+		return newState
+    }
+    if(type==='CART_CLEAR'){
+        return state={}
+    }
+    return state
+}
+
+const actionCartAdd = (good, count=1) => ({type: 'CART_ADD', count, good})
+const actionCartSub = (good, count=1) => ({type: 'CART_SUB', count, good})
+const actionCartDel = (good) => ({type: 'CART_DEL', good})
+const actionCartSet = (good, count=1) => ({type: 'CART_SET', count, good})
+const actionCartClear = () => ({type: 'CART_CLEAR'})
+
+
+const checkToken = () => {
+	const headers = {
+		"Content-Type": "application/json",
+		"Accept": "application/json",
+	}
+	if(localStorage.getItem('authToken')) {
+		return {
+			...headers,
+			"Authorization": `Bearer ${localStorage.getItem('authToken')}`
+		}
+	} else {
+		return headers;
+	}
+}
+
+const getGQL = url =>
+	(query, variables= {}) =>
+		fetch(url, {
+			method: 'POST',
+			headers: checkToken(),
+			body:JSON.stringify({query, variables})
+		}).then(res => res.json())
+			.then(data => {
+				try {
+					if(!data.data && data.errors) {
+						throw new SyntaxError(`SyntaxError - ${JSON.stringify(Object.values(data.errors)[0])}`);
+					}
+					return Object.values(data.data)[0];
+				} catch (e) {
+					console.error(e);
+				}
+			});
+
+
+
+const url = 'http://shop-roles.node.ed.asmer.org.ua/'
+
+const gql = getGQL(url + 'graphql')
+
+
+const rootCats = () => 
+actionPromise('rootCats', gql(`query rootCats2{
+CategoryFind(query: "[{\\"parent\\": null}]"){
+        _id 
+        name
+        subCategories{_id name}
+    }   
+}`))
+store.dispatch(rootCats()) 
+
+
+const categoryGoods = (_id) => 
+actionPromise('categoryGoods', gql(`query categoryGoods ($q:String) {
+    CategoryFindOne(query: $q) {
+        _id
+        name
+        parent {
+          _id
+          name
+        }
+        subCategories {
+          _id
+          name
+        }
+        goods {
+          _id
+          name
+          price
+          description
+          images {
+            url
+          }
+        }
+      }
+    }`, 
+    {q: JSON.stringify([{_id}])}
+))
+
+const Img = (_id) => 
+actionPromise('Img', gql(`query Img ($q:String) {
+    GoodFindOne (query: $q){
+        _id 
+        name
+        price
+        description 
+        images {
+        url
+        }
+}}`,
+{q: JSON.stringify([{_id}])}
+))
+
+const actionRegister = (login, password) => 
+    actionPromise('reg', gql(`mutation reg($login: String, $password: String) {
+      UserUpsert(user: {login: $login, password: $password}) {
+        _id
+        createdAt
+      }
+    }`, 
+    {"login" : login,
+    "password": password}
+    ))
+
+const actionLogin = (login, password) =>
+actionPromise('login', gql(`query log($login:String, $password:String) {
+                    login(login:$login, password:$password)
+                }`, {login, password}));
+  
+const OrderHistory = () => 
+actionPromise('OrderHistory', gql(`query historyOfOrders {
+    OrderFind(query:"[{}]") {
+    _id
+    total
+    createdAt
+    total
+    }}`,
+    {query: JSON.stringify([{}])}
+    ))
+
+const orders = () =>
+gql(`query myOrders {
+                OrderFind(query:"[{}]"){
+                _id total orderGoods{
+                    price count total good{
+                    _id name images{
+                        url
+                    }
+                    }
+                }
+                }
+            }`, {})
+
+const actionOrders = () => actionPromise('myOrders', orders())
+
+const actionOrder = () =>
+async (dispatch, getState) => {
+    const order = Object.values(getState().cart).map(orderGoods => ({good: {_id: orderGoods.good._id}, count: orderGoods.count}));
+    const newOrder = await dispatch(actionPromise('newOrder', gql(`mutation newOrder($order:OrderInput) {
+                                                        OrderUpsert(order:$order) {
+                                                        _id createdAt total
+                                                        }
+                                                    }`, {order: {orderGoods: order}})));
+    if(newOrder) {
+        dispatch(actionCartClear());
+        basket()
+    }
+}
+
+
+store.subscribe(() => {
+    const {status, payload, error} = store.getState().promise.rootCats
+    if (status === 'PENDING'){
+        main.innerHTML = `<img src='https://flevix.com/wp-content/uploads/2020/01/Bounce-Bar-Preloader-1.gif' style="width: 500px;"/>`
+    }
+    if (status === 'FULFILLED'){
+        aside.innerHTML = ''
+        for (const {_id, name} of payload){
+            aside.innerHTML += `<a href= "#/category/${_id}">${name}</a>`
+        }
+    }
+})
+
+
+store.subscribe(() => {
+    const token = store.getState().auth.token
+    if (jwtDecode(token)){
+        reg.innerHTML='<a href="#/orderhistory">Мои Заказы</a>'
+        login.innerHTML=`<button onclick="store.dispatch(actionAuthLogout())">Выйти</button>`
+    }else{
+        reg.innerHTML='<a href="#/register">Регистрация</a>'
+        login.innerHTML='<a href="#/login">Логин</a>'
+    }
+})
+    
+store.subscribe(() => {
+    const {status, payload} = store.getState().promise?.myOrders || {}
+    const [,route] = location.hash.split('/')
+    if(route !== 'orderhistory') {
+        return
+    }
+    if (status === 'PENDING'){
+        main.innerHTML = `<img src='https://flevix.com/wp-content/uploads/2020/01/Bounce-Bar-Preloader-1.gif' style="width: 500px;"/>`
+    }
+    if (status === 'FULFILLED'){
+        main.innerHTML = ''
+        let i = 1
+        for (const goods of payload){
+            let divOrders = document.createElement('div')
+            divOrders.style="border: 2px solid #ebebeb;margin: 30px;"
+            let numberOrder = document.createElement('h1')
+            numberOrder.innerText=`Заказ № ${i}\n`
+            divOrders.append(numberOrder)
+            for(const obj of goods.orderGoods){
+                const { price, count, total,good}=obj
+                const {_id,name,}=good
+
+                let div = document.createElement('div')
+                let button = document.createElement('button')
+                let a = document.createElement('a')
+                let p = document.createElement('p')
+                div.style="border: 2px solid #ebebeb;margin: 30px;"
+                a.href=`#/good/${_id}`
+                a.innerText=`${name}`
+                p.innerText=`Стоимость товара ${price} грн\nКолличество ${count} шт\nСтоимость заказа ${total} грн\n`
+                div.append(a,p)
+                button.onclick= ()=>store.dispatch(actionCartAdd({_id: _id, price:price, name:name}))
+                button.innerText='Добавить в корзину'
+                div.append(button)
+                divOrders.append(div)
+            }
+            main.prepend(divOrders)
+            i++
+        }
+        let h1 = document.createElement('h1')
+        h1.innerText='История Заказов'
+        main.prepend(h1)
+    }
+})
+
+store.subscribe(() => {
+    const {status, payload, error} = store.getState().promise?.categoryGoods || {}
+    const [,route] = location.hash.split('/')
+    if(route !== 'category') {
+        return
+    }
+    
+    if (status === 'PENDING'){
+        main.innerHTML = `<img src='https://flevix.com/wp-content/uploads/2020/01/Bounce-Bar-Preloader-1.gif' style="width: 500px;"/>`
+    }
+    
+    if (status === 'FULFILLED'){
+        main.innerHTML = ''
+        
+        const {name, goods} = payload
+        main.innerHTML = `<h1>${name}</h1>`
+        for (const good of goods){
+            const {_id, name, price, images}=good
+            let div = document.createElement('div')
+            let button = document.createElement('button')
+            let a = document.createElement('a')
+            let p = document.createElement('p')
+            div.style="border: 2px solid #ebebeb;margin: 30px;"
+            a.href=`#/good/${_id}`
+            a.innerText=`${name}`
+            p.innerText=`${price} грн`
+            div.append(a,p)
+            for (const img of images) {
+                let img1 = document.createElement('img')
+                img1.src= `${url+ img.url}`
+                div.append(img1)
+                }
+                button.onclick= ()=>store.dispatch(actionCartAdd(good))
+                button.innerText='Добавить в корзину'
+            div.append(button)
+            main.append(div)
+        }
+    }
+})
+
+
+store.subscribe(() => {
+    const {status, payload, error} = store.getState().promise?.Img || { }
+    const [,route] = location.hash.split('/')
+    if(route !== 'good') {
+        return
+    }
+    
+    if (status === 'PENDING'){
+        main.innerHTML = `<img src='https://flevix.com/wp-content/uploads/2020/01/Bounce-Bar-Preloader-1.gif' style="width: 500px;"/>`
+    }
+    
+    if (status === 'FULFILLED'){
+        main.innerHTML = ''
+        
+        const {name,price,_id, description, images} = payload
+        
+        main.innerHTML = `<h1>${name}</h1>
+        
+        <p>${description}</p>`
+        for (const img of images) {
+            main.innerHTML += `<img src= "${url+ img.url}">`
+        }
+        main.innerHTML += `<p>${price} грн</p><br>`
+        let button = document.createElement('button')
+        button.onclick=()=> store.dispatch(actionCartAdd({_id: _id, price:price, name:name}))
+        button.innerText=`Добавить в корзину`
+        main.append(button)
+    }
+})
+
+store.subscribe(() => {
+    const {cart} = store.getState()
+    let summ = 0
+    for(const {count} of Object.values(cart)) {
+        summ +=count
+    }
+    cartIcon.innerHTML = `<a href="#/cart"><b>Товаров в корзине: ${summ}</b></a>`
+})
+      
+
+basket=() => {
+    main.innerHTML = '<h1>Корзина</h1><br>'
+    const {cart} = store.getState()
+    let totalPrice = 0
+    for(let {count,good} of Object.values(cart)) {
+        const {name,price,_id}=good
+        totalPrice += price*count
+        let div = document.createElement('div')
+        let a = document.createElement('a')
+        let p = document.createElement('p')
+        let button = document.createElement('button')
+        let buttonCartSet = document.createElement('button')
+        let input = document.createElement('input')
+        input.type="number"
+        div.style="border: 2px solid #ebebeb;margin-top: 15px;margin-bottom: 15px;"
+        a.href= `#/good/${_id}`
+        a.innerText=name
+        p.innerText=`Стоимость ${price} грн\n Колличество ${count}\n Общая стоимость ${price*count} грн\n`
+        buttonCartSet.innerHTML=`Изменить количество товара<br>`
+        buttonCartSet.onclick= ()=>{
+            store.dispatch(actionCartSet({_id: _id, price:price, name:name},count=Number(input.value)));
+            basket()
+        }
+        button.onclick= ()=>{
+            store.dispatch(actionCartDel({_id: _id, price:price, name:name}));
+            basket()
+        }
+        button.innerText= 'Удалить товар'
+        main.append(div)
+        div.append(a,p,input)
+        div.append(buttonCartSet,button)
+    }
+    let checkoutDiv = document.createElement('div')
+    let buttonOrder = document.createElement('button')
+    checkoutDiv.innerText= `К оплате ${totalPrice} грн \n`
+    buttonOrder.innerText= `Оформить заказ`
+    buttonOrder.onclick= ()=>{
+        store.dispatch(actionOrder());
+        basket()
+    }
+    main.append(checkoutDiv)
+    checkoutDiv.append(buttonOrder)
+}
+
+
+
+function Password(parent, open) {
+    let inputPass = document.createElement('input')
+    inputPass.value='Пароль'
+    let inputLogin = document.createElement('input')
+    inputLogin.value='Логин'
+    let checkBox = document.createElement('input')
+    let button = document.createElement('button')
+    button.innerText='Войти'
+    button.disabled=true
+    checkBox.type = 'checkbox'
+    let div = document.createElement('div')
+
+    this.divInnerText =(text)=> div.innerText= text
+    this.buttonInnerText =(text)=> button.innerText= text
+    this.setValue =(value)=> inputPass.value = value
+    this.setOpen =(open)=> inputPass.type= open ?'text':'password'
+
+    this.getValuePass =()=> inputPass.value
+    this.getValueLogin =()=> inputLogin.value
+    this.getOpen =()=> inputPass.type
+    
+
+    checkBox.onchange =()=>this.setOpen(checkBox.checked)
+    
+    function btn (){
+        if(inputPass.value && inputLogin.value){
+            button.disabled=false
+        }
+        else{
+            button.disabled=true
+        }
+    }
+
+    inputPass.oninput = btn
+    inputLogin.oninput = btn
+    this.getbutton =(funk)=> button.onclick=funk
+    parent.append(inputLogin,inputPass,checkBox,button,div)
+}
+store.dispatch(actionAuthLogin(localStorage.authToken))
+
+
+
+window.onhashchange = () => {
+    const [,route, _id] = location.hash.split('/')
+
+    const routes = {
+        category() {
+            store.dispatch(categoryGoods(_id))
+        },
+        good(){
+          store.dispatch(Img(_id))
+        },
+        cart(){
+			basket();
+		},
+        orderhistory(){
+			store.dispatch(actionOrders())
+		},
+        login(){
+            main.innerHTML = '<h1>Вход</h1>'
+            let windowLogin = new Password(main, false)
+            windowLogin.buttonInnerText('Войти')
+            windowLogin.getbutton(
+            async () => {
+                const token = await store.dispatch(actionLogin(windowLogin.getValueLogin(), windowLogin.getValuePass()))
+                if (token){
+                    store.dispatch(actionAuthLogin(token));
+                    location.hash=''
+                }
+            })
+        },
+        register(){
+            main.innerHTML = '<h1>Регистрация</h1>'
+            let windowReg = new Password(main, false)
+            windowReg.buttonInnerText('Зарегистрироваться')
+            windowReg.getbutton(
+                async () => {
+                    const user = await store.dispatch(actionRegister(windowReg.getValueLogin(), windowReg.getValuePass()))
+                    if(user) {
+                        const token = await store.dispatch(actionLogin(windowReg.getValueLogin(), windowReg.getValuePass()))
+                        if (token){
+                            store.dispatch(actionAuthLogin(token));
+                            location.hash=''
+                        }
+                    }
+                })
+        },
+    }
+
+    if (route in routes){
+        routes[route]()
+    }
+}
+
+window.onhashchange()
+