瀏覽代碼

Module done

Andrey 2 年之前
父節點
當前提交
7364b34a80
共有 3 個文件被更改,包括 948 次插入0 次删除
  1. 221 0
      Dz16 js/Dz16js.html
  2. 二進制
      Dz16 js/img/Logo_METRO.png
  3. 727 0
      Dz16 js/index.js

+ 221 - 0
Dz16 js/Dz16js.html

@@ -0,0 +1,221 @@
+<!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>
+<style>
+    body {
+        margin: 0;
+    }
+
+    #mainContainer {
+        display: flex;
+
+    }
+
+    header {
+        display: flex;
+        background-color:blue;
+        justify-content: space-between ;
+    }
+    
+
+    #cartIcon {
+        width: 100%;
+        height: 70px;
+        display: flex;
+        
+    }
+    
+    #head{
+        display: flex;
+        align-items: center;
+    }
+
+    input {
+        height: 20px;
+        width: 100px;
+        margin-right: 10px;
+        border-radius: 10%;
+    }
+
+    #batton{
+        width: 65px;
+        height: 25px;
+        margin: 0;
+        padding: 0;
+        margin-right: 10px;
+    }
+    #battonchik {
+        width: 65px;
+        height: 25px;
+        margin: 0;
+        padding: 0;
+    }
+
+    #mainContainer{
+        background-color: yellow;
+       
+    }
+   
+    #aside {
+        width: 16.7%;
+        padding-left: 10px;
+        background-color:lawngreen;
+    }
+
+    #aside>a {
+        display: flex;
+        padding-top: 5px;
+        text-decoration: none;
+        color: black;
+    }
+    #main {
+        padding-left: 10px;
+    }
+
+    #main>a {
+        display: block;
+        padding-top: 5px;
+        text-decoration: none;
+        color: black;
+    }
+    #main>div>img{
+        max-width: 200px;
+    }
+    
+    #main>h1 {
+        margin-top: 5px;
+    } 
+    #main>p{
+        margin: 0;
+        margin-bottom: 5px;
+    } 
+
+    a>img {
+        width: 250px;
+        height: 70px;
+    }
+    main>div{
+        border: blue solid 2px;
+        margin-bottom: 10px;
+    }
+    #korzina{
+        width: 65px;
+        height: 25px;
+        margin: 0;
+        padding: 0;
+        margin-right: 10px;
+    }
+    #purchaseHistory{
+        width: 65px;
+        height: 25px;
+        margin: 0;
+        padding: 0;
+        margin-right: 10px;  
+    }
+    #battonSMakom{
+        width: 65px;
+        height: 25px;
+        margin: 0;
+        padding: 0;
+        margin-right: 10px;
+    }
+    #accaunt{
+        margin-right: 20px;
+        color: yellow;
+
+    }
+    .b-container {
+    width: 200px;
+    height: 150px;
+    background-color: #ccc;
+    margin: 0px auto;
+    padding: 10px;
+    font-size: 30px;
+    color: #fff;
+}
+.b-popup {
+    width: 100%;
+    min-height: 100%;
+    background-color: rgba(0, 0, 0, 0.5);
+    overflow: hidden;
+    position: fixed;
+    top: 0px;
+}    
+
+.b-popup .b-popup-content {
+    margin: 40px auto 0px auto;
+    width: 1000px;
+    min-height: 500px;
+    padding: 10px;
+    background-color: yellow;
+    border-radius: 5px;
+    box-shadow: 0px 0px 10px #000;
+}
+
+.b-popup-content {
+    padding: 300px;
+    display: flex;
+
+    flex-direction: column;
+}
+#buttonCloseCartId{
+    width: 30px;
+    display: flex;
+    justify-content: center;
+    align-items: flex-end;
+   margin-left: 96%;
+    
+}
+#tovar{
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 0 20px;
+}
+footer{
+    background:skyblue;
+    width: 100%;
+    height: 100px;
+}
+</style>
+</head>
+
+<body>
+    <header id="header">
+        <div id='cartIcon'>
+            <a href="#"><img src="img/Logo_METRO.png" alt=""></a>
+            
+        </div>
+        <div id="head">
+            <h1 id="accaunt"></h1>
+            <button id="korzina">Кошик</button>
+            <button id ="purchaseHistory">Історія покупок</button>
+            <input type="text" id="login">
+            <input type="password" id="password">
+            <button id="batton">Вхід</button>
+            <button id="battonSMakom">Вихід</button>
+            <button id = "battonchik">Реєстрація</button>
+        </div>
+    </header>
+    <div id='mainContainer'>
+        <aside id='aside'>
+            Категории
+        </aside>
+        <main id='main'>
+            Контент
+        </main>
+        
+        
+    </div>
+    <footer id="footer"></footer>
+    <script src='index.js'></script>
+</body>
+
+</html>
+

二進制
Dz16 js/img/Logo_METRO.png


+ 727 - 0
Dz16 js/index.js

@@ -0,0 +1,727 @@
+
+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 jwtDecode(token) {
+    try {
+        return JSON.parse(atob(token.split('.')[1]))
+    }
+    catch (e) {
+    }
+}
+
+function authReducer(state = {}, { type, token }) {
+    //{
+    //  token, payload
+    //}
+
+    if (type === 'AUTH_LOGIN') {
+        //пытаемся токен раскодировать
+        const payload = jwtDecode(token)
+        if (payload) { //и если получилось
+            return {
+                token, payload //payload - раскодированный токен;
+            }
+        }
+    }
+    if (type === 'AUTH_LOGOUT') {
+        return {}
+    }
+    return state;
+}
+
+function countReducer(state = { count: 0 }, { type }) {
+    if (type === "COUNT_INC") {
+        return {
+            count: state.count + 1
+        }
+    }
+    if (type === "COUNT_DEC") {
+        return {
+            count: state.count - 1
+        }
+    }
+    return state
+}
+
+function localStoreReducer(reducer, localStorageKey) {
+    function localStoredReducer(state, action) {
+        // Если state === undefined, то достать старый state из local storage
+        if (state === undefined) {
+            try {
+                return JSON.parse(localStorage[localStorageKey])
+            } catch (e) { }
+
+        }
+        const newState = reducer(state, action)
+        // Сохранить newState в local storage
+        localStorage[localStorageKey] = JSON.stringify(newState)
+        return newState
+
+    }
+    return localStoredReducer
+}
+
+function promiseReducer(state = {}, { type, name, status, payload, error }) {
+    ////?????
+    //ОДИН ПРОМИС:
+    //состояние: PENDING/FULFILLED/REJECTED
+    //результат
+    //ошибка:
+    //{status, payload, error}
+    //{
+    //       name1:{status, payload, error}   
+    //       name2:{status, payload, error}   
+    //       name3:{status, payload, error}   
+    //}
+    if (type === 'PROMISE') {
+        return {
+            ...state,
+            [name]: { status, payload, error }
+        }
+    }
+    return state
+}
+
+const actionPending = (name) => ({
+    type: 'PROMISE',
+    status: 'PENDING',
+    name
+})
+const actionFulfilled = (name, payload) => ({
+    type: 'PROMISE',
+    status: 'FULFILLED',
+    name,
+    payload
+})
+const actionRejected = (name, error) => ({
+    type: 'PROMISE',
+    status: 'REJECTED',
+    name,
+    error
+})
+
+const actionPromise = (name, promise) =>
+    async dispatch => {
+        try {
+            dispatch(actionPending(name))
+            let payload = await promise
+            dispatch(actionFulfilled(name, payload))
+            return payload
+        }
+        catch (e) {
+            dispatch(actionRejected(name, e))
+        }
+    }
+
+const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
+
+
+function combineReducers(reducers) { //пачку редьюсеров как объект {auth: authReducer, promise: promiseReducer}
+    function combinedReducer(combinedState = {}, action) { //combinedState - типа {auth: {...}, promise: {....}}
+        const newCombinedState = {}
+        for (const [reducerName, reducer] of Object.entries(reducers)) {
+            const newSubState = reducer(combinedState[reducerName], action)
+            if (newSubState !== combinedState[reducerName]) {
+                newCombinedState[reducerName] = newSubState
+            }
+        }
+        if (Object.keys(newCombinedState).length === 0) {
+            return combinedState
+        }
+        return { ...combinedState, ...newCombinedState }
+    }
+
+    return combinedReducer //нам возвращают один редьюсер, который имеет стейт вида {auth: {...стейт authReducer-а}, promise: {...стейт promiseReducer-а}}
+}
+
+function cartReducer(state = {}, { type, count = 1, good }) {
+    // type CART_ADD CART_REMOVE CART_CLEAR CART_DEL
+    // {
+    //  id1: {count: 1, good: {name, price, images, id}}
+    // }
+    if (type === "CART_ADD") {
+        return {
+            ...state,
+            [good._id]: { count: count + (state[good._id]?.count || 0), good },
+        };
+    }
+
+    if (type === "CART_DELETE") {
+        if (state[good._id].count > 1) {
+            return {
+                ...state,
+                [good._id]: {
+                    count: -count + (state[good._id]?.count || 0),
+                    good,
+                },
+            };
+        }
+
+        if (state[good._id].count === 1) {
+            let { [good._id]: id1, ...newState } = state; //o4en strashnoe koldunstvo
+            //delete newState[good._id]
+            return newState;
+        }
+    }
+
+    if (type === "CART_CLEAR") {
+        return {};
+    }
+    if (type === "CART_REMOVE") {
+        // let newState = {...state}
+        let { [good._id]: id1, ...newState } = state; //o4en strashnoe koldunstvo
+        //delete newState[good._id]
+        return newState;
+    }
+
+    return state;
+}
+
+
+
+
+const backendURL = 'http://shop-roles.node.ed.asmer.org.ua/'
+//store.dispatch(actionPromise('delay1000', delay(1000))) 
+//store.dispatch(actionPromise('delay3000', delay(3000))) 
+
+//store.dispatch(actionPending('delay1000'))
+
+//delay(1000).then(result => store.dispatch(actionFulfilled('delay1000', result)),
+//error => store.dispatch(actionRejected('delay1000', error)))
+
+//store.dispatch(actionPending('delay3000'))
+
+//delay(3000).then(result => store.dispatch(actionFulfilled('delay3000', result)),
+//error => store.dispatch(actionRejected('delay3000', error)))
+
+const getGQL = (url) => (query, variables) =>
+    fetch(url, {
+        method: "POST",
+        headers: {
+            "Content-Type": "application/json",
+            ...(localStorage.authToken
+                ? { Authorization: "Bearer " + localStorage.authToken }
+                : {}),
+        },
+        body: JSON.stringify({ query, variables }),
+    })
+        .then((res) => res.json())
+        .then((data) => {
+            if (data.data) {
+                return Object.values(data.data)[0];
+            } else throw new Error(JSON.stringify(data.errors));
+        });
+
+const gql = getGQL(backendURL + "graphql");
+
+// const gql = (url, query, variables) => fetch(url, {
+//     method: 'POST',
+//     headers: {
+//         "Content-Type": "application/json",
+//         Accept: "application/json",
+//     },
+//     body: JSON.stringify({ query, variables })
+// }).then(res => res.json())
+
+// const backendURL = 'http://shop-roles.node.ed.asmer.org.ua/graphql'
+
+
+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){
+                    _id name goods {
+                        _id name price images {
+                            url
+                }
+            }
+        }
+    }`,
+            { q: JSON.stringify([{ _id }]) }
+        )
+    )
+
+const actionGoodById = (_id) =>
+    actionPromise(
+        'goodByID',
+        gql(
+
+            `query goodByID($q:String){
+                GoodFindOne(query: $q){
+                    _id
+                    name
+  	                description
+                    price
+                    categories{
+                        _id
+                        name
+                    }
+                    images{
+                        url
+                    }
+                }
+            }`,
+            { q: JSON.stringify([{ _id }]) }
+        )
+    )
+
+const actionRegistr = (login, password) =>
+    actionPromise(
+        'registr',
+        gql(
+
+            `mutation register($login:String, $password:String){
+                UserUpsert(user: {login:$login, password:$password}){
+                  _id login 
+                }
+              }`,
+            { login: login, password: password }
+        )
+    )
+
+const actionLogin = (login, password) =>
+    actionPromise(
+        'login',
+        gql(
+
+            `query log($login:String, $password:String){
+                login(login:$login, password:$password)
+            }`,
+            { login: login, password: password }
+        )
+    )
+
+    const actionOrder = () => async (dispatch, getState) => {
+        let { cart } = getState();
+        const orderGoods = Object.entries(cart).map(([_id, { count }]) => ({
+            good: { _id },
+            count,
+        }));
+    
+        let result = await dispatch(
+            actionPromise(
+                "order",
+                gql(
+                    `
+                        mutation newOrder($order:OrderInput){
+                        OrderUpsert(order:$order)
+                            { _id total  }
+                        }
+                `,
+                    { order: { orderGoods } }
+                )
+            )
+        );
+        if (result?._id) {
+            dispatch(actionCartClear());
+            document.location.hash = "#/cart/";
+            alert("Покупка успішна")
+        }
+    
+    };
+
+    const orderHistory = () =>
+    actionPromise(
+        "history",
+        gql(` query OrderFind{
+        OrderFind(query:"[{}]"){
+            _id total createdAt orderGoods{
+                count good{
+                    _id name price images{
+                        url
+                    }
+                }
+                owner{
+                    _id login 
+                }
+            }
+        }
+    }
+    `)
+    );
+
+const actionAuthLogin = (token) =>
+    (dispatch, getState) => {
+        const oldState = getState()
+        dispatch({ type: 'AUTH_LOGIN', token })
+        const newState = getState()
+        if (oldState !== newState)
+            localStorage.authToken = token
+    }
+
+const actionAuthLogout = () =>
+    dispatch => {
+        dispatch({ type: 'AUTH_LOGOUT' })
+        localStorage.removeItem('authToken')
+    }
+
+const actionCartAdd = (good, count = 1) => ({
+    type: "CART_ADD",
+    good,
+    count
+});
+const actionCartChange = (good, count = 1) => ({
+    type: "CART_CHANGE",
+    good,
+    count,
+}); ///oninput меняяем полностью
+const actionCartDelete = (good) => ({
+    type: "CART_DELETE",
+    good
+});
+const actionCartClear = () => ({
+    type: "CART_CLEAR"
+});
+
+const actionFullLogin = (login, password) => async (dispatch) => {
+    let token = await dispatch(actionLogin(login, password))
+    if (token) {
+        dispatch(actionAuthLogin(token))
+    }
+}
+
+const actionFullRegistr = (login, password) => async (dispatch) => {
+    try {
+        await dispatch(actionRegistr(login, password))
+    }
+    catch (e) {
+        return console.log(e)
+    }
+    await dispatch(actionFullLogin(login, password))
+}
+
+const store = createStore(combineReducers({ auth: authReducer, promise: promiseReducer, cart: localStoreReducer(cartReducer, "cart") })) //не забудьте combineReducers если он у вас уже есть
+if (localStorage.authToken) {
+    store.dispatch(actionAuthLogin(localStorage.authToken))
+}
+//const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer}))
+store.subscribe(() => console.log(store.getState()))
+
+store.dispatch(actionRootCats())
+// store.dispatch(actionLogin('test456', '123123'))
+
+
+
+store.subscribe(() => {
+    const { rootCats } = store.getState().promise
+    if (rootCats?.payload) {
+        aside.innerHTML = ''
+        for (let { _id, name } of rootCats?.payload) {
+            const a = document.createElement('a')
+            a.href = `#/category/${_id}`
+            a.innerHTML = name
+            aside.append(a)
+        }
+    }
+})
+
+
+store.subscribe(() => {
+    const { catById } = store.getState().promise
+    const [, route, _id] = location.hash.split('/')
+    if (catById?.payload && route === 'category') {
+        const { name, goods, _id } = catById?.payload
+        main.innerHTML = `<h1>${name}</h1>`
+        for (let { _id, name, price, images } of goods) {
+            const cards = document.createElement("div")
+            cards.innerHTML = `<h2>${name}</h2>
+                                <img src="${backendURL}/${images[0].url}"/>    
+                                 <strong>Ціна ${price} грн</strong>                          
+                                 <a href="#/good/${_id}">Перейти</a> `;
+            main.append(cards);
+
+
+
+            // const a = document.createElement('a')
+            // const p = document.createElement('p')
+            // p.href = `#/good/${_id}`
+            // a.href  = `#/good/${_id}`
+            // a.innerHTML = name
+            // p.innerHTML = price
+            // main.append(a)
+            // main.append(p)
+        }
+    }
+})
+
+
+store.subscribe(() => {
+    const { goodByID } = store.getState().promise
+    const [, route, _id] = location.hash.split('/')
+    if (goodByID?.payload && route === 'good') {
+        main.innerHTML = ""
+        const { name, images, price, description } = goodByID?.payload
+        // main.innerHTML = `<h1>${name}</h1> Продукт`
+        const cards = document.createElement("div")
+        cards.innerHTML = `<h2>${name}</h2>
+                            <img src="${backendURL}/${images[0].url}"/> 
+                            <strong> Ціна ${price} грн</strong>
+                            <button id='buy'> Придбати</button>
+                            <p>${description}</p>`;
+        main.append(cards)
+        cards.style.marginTop = "10px"
+        var btn = document.getElementById('buy')
+        btn.onclick = () => {
+            store.dispatch(actionCartAdd(goodByID.payload))
+
+        }
+    }
+})
+
+
+const bPopupContent = document.createElement("div");
+const obertkaDlyaTovara = document.createElement("div")
+
+const all = document.createElement('h2')
+const checkout = document.createElement("button")
+const clearToCart = document.createElement("button")
+store.subscribe(() => {
+    obertkaDlyaTovara.innerHTML = ""
+    const cartById = store.getState().cart
+    let productCount = 0;
+    let productPrice = 0
+    for (let gPC of Object.values(cartById)) {
+        const { good,count } = gPC
+        productCount += count
+        productPrice += good.price * count
+        const tovar = document.createElement("div")
+        tovar.id = "tovar"
+        tovar.style.border = "3px solid blue"
+        tovar.style.marginTop = "10px"
+        const name = document.createElement('h1')
+        const price = document.createElement('h3')
+        const countById = document.createElement('p')
+        const divDlyaKnopok = document.createElement("div")
+        const plus = document.createElement("button")
+        const minus = document.createElement("button")
+        plus.innerText = "+"
+        minus.innerText = "-"
+        tovar.append(name)
+        tovar.append(price)
+        tovar.append(countById)
+        divDlyaKnopok.append(plus)
+        divDlyaKnopok.append(minus)
+        tovar.append(divDlyaKnopok)
+        name.innerHTML = good.name
+        price.innerHTML = good.price
+        countById.innerHTML = count
+        obertkaDlyaTovara.append(tovar)
+        bPopupContent.append(obertkaDlyaTovara)
+        
+        plus.onclick = () => {
+            store.dispatch(actionCartAdd(good))
+        }
+        minus.onclick = () => {
+            store.dispatch(actionCartDelete(good))
+        }
+        
+    }
+        clearToCart.id = "clearToCart"
+        clearToCart.innerHTML = "Очистити кошик"
+        clearToCart.style.margin = "0 auto"
+        clearToCart.style.marginBottom = "20px"
+        clearToCart.style.background = "blue"
+        clearToCart.style.color = "yellow"
+        bPopupContent.append(clearToCart)
+        checkout.id = "checkout"
+        checkout.innerHTML = "Оформити замовлення"
+        checkout.style.margin = "0 auto"
+        checkout.style.background = "blue"
+        checkout.style.color = "yellow"
+        bPopupContent.append(checkout)
+        all.id = "all"
+        all.innerHTML = "Всього: " + productPrice
+        bPopupContent.append(all)
+        all.style.marginLeft = "90%"
+        clearToCart.onclick = () => {
+            all.innerHTML = " "
+            store.dispatch(actionCartClear())
+            
+        }
+
+        checkout.onclick = () => {
+            all.innerHTML = " "
+            store.dispatch(actionOrder());
+            store.dispatch(orderHistory());
+            
+        }
+    })
+
+
+
+store.subscribe(() => {
+    batton.onclick = () => {
+        store.dispatch(actionFullLogin(login.value, password.value))
+
+    }
+    battonchik.onclick = () => {
+        store.dispatch(actionFullRegistr(login.value, password.value))
+
+    }
+
+    battonSMakom.onclick = () => {
+        store.dispatch(actionAuthLogout())
+
+    }
+    const payload = store.getState().auth.token;
+    if (payload) {
+        korzina.style.display = "block"
+        login.style.display = "none"
+        password.style.display = "none"
+        batton.style.display = "none"
+        battonchik.style.display = "none"
+        battonSMakom.style.display = "block"
+        accaunt.style.display = "block"
+        accaunt.innerText = jwtDecode(payload).sub.login;
+        purchaseHistory.style.display = "block"
+
+    } else {
+        korzina.style.display = "none"
+        battonSMakom.style.display = "none"
+        login.style.display = "block"
+        password.style.display = "block"
+        batton.style.display = "block"
+        battonchik.style.display = "block"
+        accaunt.style.display = "none"
+        purchaseHistory.style.display = "none"
+    }
+
+})
+store.dispatch(orderHistory());
+const h2 = document.createElement("h2")
+store.subscribe(() => {
+    const { history } = store.getState().promise;
+    const [, route] = location.hash.split("/");
+    purchaseHistory.onclick = () => {
+        const bPopup = document.createElement("div");
+        const bPopupContent = document.createElement("div");
+        bPopup.id = "b-popup";
+        bPopup.className = "b-popup";
+        bPopupContent.className = "b-popup-content b-poput-container-flex";
+        header.append(bPopup);
+        bPopup.append(bPopupContent);
+        const buttonCloseCart = document.createElement("button");
+        buttonCloseCart.innerText = "×";
+        buttonCloseCart.id = "buttonCloseCartId";
+        bPopupContent.append(buttonCloseCart);
+        buttonCloseCart.onclick = () => {
+            var parent = document.getElementById("header");
+            var child = document.getElementById("b-popup");
+            parent.removeChild(child);
+        };
+       
+
+        for (let [key, value] of Object.entries(history.payload)) {
+            const { _id, createdAt, total, orderGoods } = value;
+            const h2 = document.createElement("h2");
+            h2.className = "h2History"
+            const dateOfOrder = new Date(+createdAt);
+            h2.innerHTML = `${dateOfOrder.toLocaleDateString()} ${dateOfOrder.toLocaleTimeString()}
+                            Order ID: ${_id} от , c ${orderGoods.length} goods worth: ${total}`;
+                            bPopupContent.append(h2);
+        
+            
+        }
+
+        if (Object.keys(history.payload).length == 0) {
+            const p = document.createElement("p");
+            p.innerHTML = "<p>Ще немає покупок</p>";
+            card.append(p);
+        }
+
+       
+    };
+    
+});
+
+
+const buttonCloseCart = document.createElement("button");
+buttonCloseCart.innerText = `×`;
+buttonCloseCart.id = "buttonCloseCartId";
+bPopupContent.append(buttonCloseCart);
+
+buttonCloseCart.onclick = () => {
+    var parent = document.getElementById("header");
+    var child = document.getElementById("b-popup");
+    parent.removeChild(child);
+};
+function bPopupCreate() {
+    const bPopup = document.createElement("div");
+    bPopup.id = "b-popup";
+    bPopup.className = "b-popup";
+    bPopupContent.className = "b-popup-content b-poput-container-flex";
+    header.append(bPopup);
+    bPopup.append(bPopupContent);
+}
+korzina.onclick = () => {
+    bPopupCreate()
+}
+
+
+
+window.onhashchange = () => {
+    const [, route, _id] = location.hash.split('/')
+    console.log(route, _id)
+    const routes = {
+        category() {
+            store.dispatch(actionCatById(_id))
+        },
+        good() {
+            store.dispatch(actionGoodById(_id))
+        },
+        dashboard() {
+            store.dispatch(orderHistory());
+        },
+    }
+
+    if (route in routes) {
+        routes[route]()
+    }
+
+}
+
+window.onhashchange()
+
+
+
+