Procházet zdrojové kódy

Market <no purchase history> done

Illia Kozyr před 2 roky
rodič
revize
75613f8b41

+ 5 - 2
Market GraphQL + Redux/index.html

@@ -24,13 +24,16 @@
                     <button id="reg">Registration</button>
                 </div>
                 <div>
-                    <button id="login">login</button>
+                    <button id="login">Login</button>
+                </div>
+                <div>
+                    <button id="purchaseHistory">Purchase History</button>
                 </div>
             </div>
         </header>
         <main id="mainContainer">
             <aside id="aside">Категории</aside>
-            <div class="mainBlock">
+            <div class="mainBlock" id="mainBlock">
                 <div id="categoryName"></div>
 
                 <div id="productBlock"></div>

+ 236 - 126
Market GraphQL + Redux/script.js

@@ -1,6 +1,3 @@
-const backendURL = "http://shop-roles.node.ed.asmer.org.ua/graphql";
-const backendURLNotGraphQL = "http://shop-roles.node.ed.asmer.org.ua";
-
 function createStore(reducer) {
     let state = reducer(undefined, {}); //стартовая инициализация состояния, запуск редьюсера со state === undefined
     let cbs = []; //массив подписчиков
@@ -37,33 +34,28 @@ function jwtDecode(token) {
     } catch (e) {}
 }
 
-function authReducer(state = {}, { type, token }) {
-    //{
-    //  token, payload
-    //}
-
+function authReducer(state, { type, token }) {
+    if (state === undefined) {
+        if (localStorage.authToken) {
+            type = "AUTH_LOGIN";
+            token = localStorage.authToken;
+        }
+    }
     if (type === "AUTH_LOGIN") {
-        //пытаемся токен раскодировать
-        const payload = jwtDecode(token);
+        let payload = jwtDecode(token);
         if (payload) {
-            return {
-                token,
-                payload, //payload - раскодированный токен;
-            };
+            localStorage.authToken = token;
+            return { token, payload };
         }
     }
     if (type === "AUTH_LOGOUT") {
+        localStorage.removeItem("authToken");
         return {};
     }
-    return state;
+    return state || {};
 }
 
-const actionAuthLogin = (token) => (dispatch, getState) => {
-    const oldState = getState();
-    dispatch({ type: "AUTH_LOGIN", token });
-    const newState = getState();
-    if (oldState !== newState) localStorage.authToken = token;
-};
+const actionAuthLogin = (token) => ({ type: "AUTH_LOGIN", token });
 
 const actionAuthLogout = () => (dispatch) => {
     dispatch({ type: "AUTH_LOGOUT" });
@@ -120,6 +112,14 @@ function cartReducer(state = {}, { type, count = 1, good }) {
             [good._id]: { count: count + (state[good._id]?.count || 0), good },
         };
     }
+
+    if (type === "CART_DELETE") {
+        return {
+            ...state,
+            [good._id]: { count: -count + (state[good._id]?.count || 0), good },
+        };
+    }
+
     if (type === "CART_CLEAR") {
         return {};
     }
@@ -180,6 +180,28 @@ function combineReducers(reducers) {
     return combinedReducer; //нам возвращают один редьюсер, который имеет стейт вида {auth: {...стейт authReducer-а}, promise: {...стейт promiseReducer-а}}
 }
 
+const getGQL = (url) => (query, variables) =>
+    fetch(url, {
+        method: "POST",
+        headers: {
+            "Content-Type": "application/json",
+            // 'Accept' : '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 backendURL = "http://shop-roles.node.ed.asmer.org.ua";
+const gql = getGQL(backendURL + "/graphql");
+
 const store = createStore(
     combineReducers({
         auth: authReducer,
@@ -193,22 +215,25 @@ if (localStorage.authToken) {
 //const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer}))
 store.subscribe(() => console.log(store.getState()));
 
-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 backendURLNotGraphQL = "http://shop-roles.node.ed.asmer.org.ua";
+
+// 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 actionRootCats = () =>
     actionPromise(
         "rootCats",
         gql(
-            backendURL,
             `query {
+                
 CategoryFind(query: "[{\\"parent\\":null}]"){
 _id name
 }
@@ -216,16 +241,16 @@ _id name
         )
     );
 
-const actionCatById = (
-    _id //добавить подкатегории
-) =>
+const actionCatById = (_id) =>
     actionPromise(
         "catById",
         gql(
-            backendURL,
             `query catById($q: String){
 CategoryFindOne(query: $q){
-_id name goods {
+_id name subCategories {
+    name _id
+}
+goods {
     _id name price images {
         url
     }
@@ -240,7 +265,6 @@ const actionLogin = (login, password) =>
     actionPromise(
         "actionLogin",
         gql(
-            backendURL,
             `query log($login:String, $password:String){
                                   login(login:$login, password:$password)
                                 }`,
@@ -252,7 +276,6 @@ const actionGoodById = (_id) =>
     actionPromise(
         "GoodFineOne",
         gql(
-            backendURL,
             `query goodByid($goodId: String) {
         GoodFindOne(query: $goodId) {
             _id
@@ -270,10 +293,20 @@ const actionGoodById = (_id) =>
 
 store.dispatch(actionRootCats());
 
-const actionFullLogin = (login, password) => async (dispatch) => {
-    let result = await dispatch(actionLogin(login, password));
-    if (result.data.login) {
-        dispatch(actionAuthLogin(result.data.login));
+const actionFullLogin = (log, pass) => async (dispatch) => {
+    let token = await dispatch(
+        actionPromise(
+            "login",
+            gql(
+                `query login($login: String, $password: String) {
+            login(login: $login, password: $password)
+            }`,
+                { login: log, password: pass }
+            )
+        )
+    );
+    if (token) {
+        dispatch(actionAuthLogin(token));
     }
 };
 
@@ -282,7 +315,6 @@ const actionFullRegister = (login, password) => async (dispatch) => {
         actionPromise(
             "register",
             gql(
-                backendURL,
                 `mutation register($login: String, $password: String) {
                 UserUpsert(user: {login: $login, password: $password}) {
                    _id
@@ -298,11 +330,40 @@ const actionFullRegister = (login, password) => async (dispatch) => {
     }
 };
 
+const actionNewOrder = () => async (dispatch, getState) => {
+    const { cart } = getState();
+    let order = { orderGoods: [] };
+    for (let [key, value] of Object.entries(cart)) {
+        let newValue = { ...value };
+
+        let { name, price, images, ...id } = newValue.good;
+        newValue.good = id;
+        order.orderGoods.push({ ...newValue });
+    }
+
+    let newOrder = await dispatch(
+        actionPromise(
+            "newOrder",
+            gql(
+                `mutation newOrder($order: OrderInput) {
+                OrderUpsert(order: $order) {
+                _id
+                total
+                }
+            }`,
+                { order: order }
+            )
+        )
+    );
+    if (newOrder) {
+        dispatch(actionCartClear());
+    }
+};
+
 const actionOrders = () =>
     actionPromise(
         "orders",
         gql(
-            backendURL,
             `query findOrder($q: String) {
     OrderFind(query: $q) {
       _id
@@ -322,11 +383,10 @@ const actionOrders = () =>
     );
 
 store.subscribe(() => {
-    const rootCats =
-        store.getState().promise.rootCats?.payload?.data.CategoryFind;
-    if (rootCats) {
+    const { rootCats } = store.getState().promise;
+    if (rootCats?.payload) {
         aside.innerHTML = "";
-        for (let { _id, name } of rootCats) {
+        for (let { _id, name } of rootCats?.payload) {
             const a = document.createElement("a");
             a.href = `#/category/${_id}`;
             a.innerHTML = name;
@@ -336,12 +396,11 @@ store.subscribe(() => {
 });
 
 store.subscribe(() => {
-    const catById =
-        store.getState().promise.catById?.payload?.data.CategoryFindOne;
+    const { catById } = store.getState().promise;
     const [, route] = location.hash.split("/");
 
-    if (catById && route === "category") {
-        const { name, goods, _id } = catById;
+    if (catById?.payload && route === "category") {
+        const { name, goods, subCategories } = catById?.payload;
         categoryName.innerHTML = `<h1>${name}</h1>`;
 
         var element = document.getElementById("productBlock");
@@ -349,13 +408,22 @@ store.subscribe(() => {
             element.removeChild(element.firstChild);
         }
 
+        if (subCategories) {
+            for (let { name, _id } of subCategories) {
+                const link = document.createElement("a");
+                link.id = "subCategories";
+                link.href = `#/category/${_id}`;
+                link.innerText = name;
+                productBlock.append(link);
+            }
+        }
+
         for (let { _id, name, price, images } of goods) {
             const description = document.createElement("div");
             const textBlock = document.createElement("div");
             const imgProduct = document.createElement("img");
             const a = document.createElement("p");
             const productPrice = document.createElement("p");
-            const b = document.getElementById(productBlock);
             const linkCard = document.createElement("a");
 
             productBlock.append(linkCard);
@@ -363,14 +431,14 @@ store.subscribe(() => {
 
             linkCard.append(description);
             description.setAttribute("class", "card");
+            description.id = "card";
 
             description.append(imgProduct);
 
-            imgProduct.src = `http://shop-roles.node.ed.asmer.org.ua/${images[0].url}`;
+            imgProduct.src = `${backendURL}/${images[0].url}`;
 
             description.append(textBlock);
 
-            // a.href = `#/good/${_id}`;
             a.innerHTML = name;
             textBlock.append(a);
 
@@ -385,6 +453,51 @@ store.subscribe(() => {
     }
 });
 
+const flexBlockForGFO = document.createElement("div");
+flexBlockForGFO.id = "flexBlockForGFO";
+const goodFineOneImgBlock = document.createElement("div");
+const goodFineOneTextBlock = document.createElement("div");
+const goodFineOneName = document.createElement("h2");
+const goodFineOneImg = document.createElement("img");
+const goodFineOnePrice = document.createElement("p");
+const goodFineOneDescription = document.createElement("p");
+const goodFineOneAddToCartButton = document.createElement("button");
+const buttonPlus = document.createElement("button");
+const buttonMinus = document.createElement("button");
+buttonPlus.innerHTML = "+";
+buttonMinus.innerHTML = "-";
+
+store.subscribe(() => {
+    const { GoodFineOne } = store.getState().promise;
+    const [, route, _id] = location.hash.split("/");
+    if (GoodFineOne?.payload && route === "good") {
+        productBlock.innerHTML = "";
+        const { name, images, price, description } = GoodFineOne?.payload;
+        productBlock.append(flexBlockForGFO);
+
+        flexBlockForGFO.append(goodFineOneImgBlock);
+        goodFineOneImg.src = `${backendURL}/${images[0].url}`;
+        goodFineOneImg.id = "goodOneImg";
+        goodFineOneImgBlock.append(goodFineOneImg);
+
+        flexBlockForGFO.append(goodFineOneTextBlock);
+
+        goodFineOneName.innerHTML = name;
+        goodFineOneTextBlock.append(goodFineOneName);
+
+        goodFineOnePrice.innerHTML = "price: " + price;
+        goodFineOneTextBlock.append(goodFineOnePrice);
+        goodFineOneDescription.innerHTML = description;
+        goodFineOneTextBlock.append(goodFineOneDescription);
+        goodFineOneAddToCartButton.innerHTML = "add to cart";
+        goodFineOneTextBlock.append(goodFineOneAddToCartButton);
+
+        goodFineOneAddToCartButton.onclick = () => {
+            store.dispatch(actionCartAdd(GoodFineOne.payload));
+        };
+    }
+});
+
 const bPoputDeleteBlock = document.createElement("div");
 const bPoput = document.createElement("div");
 bPoput.className = "b-popup";
@@ -412,14 +525,24 @@ buttonGoodDeleteBlock.append(buttonGoodDelete);
 bPoputContainer.append(buttonCloseCart);
 
 const divToCardBlock = document.createElement("div");
+const goodByIdPrice = document.createElement("h2");
+const buyBlock = document.createElement("div");
+buyBlock.className = "buyBlock";
+const buttonBuy = document.createElement("button");
+buttonBuy.className = "buttonBuy";
+buttonBuy.id = "buttonBuy";
 
 store.subscribe(() => {
     divToCardBlock.innerHTML = "";
-    toCartById = store.getState().cart;
+    goodByIdPrice.innerHTML = "";
+    const toCartById = store.getState().cart;
     let countSum = 0;
+    let priceSum = 0;
     for (let value of Object.values(toCartById)) {
-        const { count, good } = value;
+        const { count, good, price } = value;
+
         countSum += count;
+        priceSum += good.price * count;
 
         divToCardBlock.id = "divToCartBlock";
         const divToCart = document.createElement("div");
@@ -429,6 +552,12 @@ store.subscribe(() => {
         const goodByIdCount = document.createElement("h2");
         const buttonPlus = document.createElement("button");
         const buttonMinus = document.createElement("button");
+
+        buttonBuy.style.display = "block";
+        buttonBuy.innerHTML = "Buy";
+
+        goodByIdPrice.innerHTML = "Total: " + priceSum;
+
         buttonPlus.innerHTML = "+";
         buttonMinus.innerHTML = "-";
         buttonPlus.id = "buttonPlus";
@@ -442,12 +571,21 @@ store.subscribe(() => {
         divToCart.append(goodByIdCount);
         divToCart.append(buttonPlus);
         divToCart.append(buttonMinus);
+        bPoputContainer.append(buyBlock);
+        buyBlock.append(goodByIdPrice);
+        buyBlock.append(buttonBuy);
 
-        goodByIdImage.src = `${backendURLNotGraphQL}/${value.good.images[0].url}`;
+        goodByIdImage.src = `${backendURL}/${value.good.images[0].url}`;
         goodByIdName.innerText = good.name;
         goodByIdCount.innerText = count;
 
+        buttonBuy.onclick = () => {
+            store.dispatch(actionNewOrder());
+        };
         
+        buttonPlus.onclick = () => store.dispatch(actionCartAdd(value.good));
+        buttonMinus.onclick = () =>
+            store.dispatch(actionCartDelete(value.good));
     }
 
     shoppingCart.innerHTML = "Cart: " + countSum;
@@ -472,74 +610,11 @@ buttonGoodDelete.onclick = () => {
     a.innerHTML = "";
     let b = document.getElementById("shoppingCart");
     b.innerHTML = "Cart";
+    let c = document.getElementById("buttonBuy");
+    c.style.display = "none";
 };
 
-const buyButtom = document.createElement("button");
-const productImg = document.createElement("img");
-const productName = document.createElement("h1");
-const productPrice = document.createElement("h2");
-const textBlock = document.createElement("div");
-const flexBlock = document.createElement("div");
-const productDescription = document.createElement("p");
-
-store.subscribe(() => {
-    const goodById =
-        store.getState().promise.GoodFineOne?.payload?.data.GoodFindOne;
-    const [, route, _id] = location.hash.split("/");
-
-    if (goodById && route === "good") {
-        var element = document.getElementById("productBlock");
-        while (element.firstChild) {
-            element.removeChild(element.firstChild);
-        }
-        const { name, price, description, images } = goodById;
-
-        flexBlock.id = "flexBlock";
-
-        productBlock.append(flexBlock);
-
-        flexBlock.append(productImg);
-        productImg.style.width = "500px";
-        productImg.style.height = "500px";
-        productImg.src = `http://shop-roles.node.ed.asmer.org.ua/${images[0].url}`;
-
-        textBlock.id = "textBlock";
-        flexBlock.append(textBlock);
-
-        productName.innerHTML = name;
-        textBlock.append(productName);
-
-        productPrice.innerHTML = "price: " + price;
-        textBlock.append(productPrice);
-
-        productDescription.innerHTML = description;
-        textBlock.append(productDescription);
-
-        buyButtom.id = "buyButtom";
-        buyButtom.innerHTML = "Add to cart";
-        textBlock.append(buyButtom);
-        buyButtom.onclick = () => {
-            store.dispatch(actionCartAdd(goodById));
-        };
-       
-        
-    }
-});
-
-
-
-
-
-store.subscribe(() => {
-    const catById =
-        store.getState().promise.catById?.payload?.data.CategoryFindOne;
-    const [, route, _id] = location.hash.split("/");
-
-    if (catById && route === "good") {
-        const { name, price, description, images } = catById;
-        categoryName.innerHTML = `<h1>${name}</h1>`;
-    }
-});
+const goodByIdName = document.createElement("div");
 
 const h2text = document.createElement("h2");
 h2text.id = "h2text";
@@ -551,7 +626,6 @@ qwer.append(logoutButton);
 store.subscribe(() => {
     const payload = store.getState().auth.token;
     if (payload) {
-        buyButtom.style.display = "block";
         logoutButton.style.display = "block";
         logoutButton.innerHTML = "Logout";
         login.style.display = "none";
@@ -561,12 +635,16 @@ store.subscribe(() => {
 
         h2text.innerText = jwtDecode(payload).sub.login;
     } else {
-        buyButtom.style.display = "none";
         h2text.style.display = "none";
         logoutButton.style.display = "none";
     }
 });
 
+// store.subscribe(() => {
+//     const orders = store.dispatch()
+
+// })
+
 const buttonLogin = document.createElement("button");
 buttonLogin.id = "loginInputt";
 buttonLogin.innerText = "Login";
@@ -613,6 +691,30 @@ function bPopupCreate(text) {
     };
 }
 
+// store.subscribe(() => {
+//     dashboardUl.innerHTML = ''
+//     const {orders} = store.getState().promise;
+//     const [,route, _id] = location.hash.split('/');
+//     if(orders?.payload && route === 'dashboard'){
+//         for(let {createdAt, total, orderGoods} of orders.payload){
+//             let date = new Date(+createdAt);
+//             let li = document.createElement("li");
+//             for(let {count, good} of orderGoods){
+//                 let div = document.createElement("div");
+//                 div.innerHTML = `<strong>${good.name}</strong>
+//                                 <span>${count} &#10006; ${good.price}</span>
+//                                 `
+//                 li.append(div);
+//             }
+//             li.innerHTML += `<div>${total}</div>
+//             <div>${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}</div>
+//             <hr>`
+//             dashboardUl.append(li)
+//         }
+//     }
+
+// })
+
 window.onhashchange = () => {
     const [, route, _id] = location.hash.split("/");
 
@@ -634,6 +736,8 @@ window.onhashchange = () => {
     }
 };
 
+window.onhashchange();
+
 login.onclick = () => {
     bPopupCreate(buttonLogin);
     buttonLogin.onclick = () => {
@@ -643,6 +747,7 @@ login.onclick = () => {
         var parent = document.getElementById("header");
         var child = document.getElementById("b-popup");
         parent.removeChild(child);
+        purchaseHistory.style.display = "block";
     };
 };
 
@@ -662,4 +767,9 @@ logoutButton.onclick = () => {
     store.dispatch(actionAuthLogout());
     login.style.display = "block";
     reg.style.display = "block";
+    purchaseHistory.style.display = "none";
 };
+
+// purchaseHistory.onclick = () => {
+
+// }

+ 76 - 46
Market GraphQL + Redux/style.css

@@ -8,9 +8,8 @@ header {
     background-color: yellow;
     height: 100px;
     max-width: 1800px;
-    
 }
-.qwer{
+.qwer {
     display: flex;
     align-items: center;
 }
@@ -18,18 +17,18 @@ button {
     height: 50px;
     width: 150px;
     margin-right: 30px;
-    background-color:aqua;
+    background-color: aqua;
     border-radius: 15px;
-    color:darkblue;
+    color: darkblue;
     font-size: 20px;
 }
 
 button:hover {
     transition: 300ms;
-   background-color: aliceblue;
+    background-color: aliceblue;
 }
 
-input{
+input {
     height: 20px;
     width: 100px;
     margin-right: 30px;
@@ -76,7 +75,11 @@ a:hover {
 
 #productPicture {
     width: 200px;
-    
+}
+
+#productBlock {
+    display: flex;
+    flex-wrap: wrap;
 }
 
 #productBlock img {
@@ -84,16 +87,44 @@ a:hover {
     height: 300px;
 }
 
+#productBlock h2 {
+    margin-top: 60px;
+}
+
+#flexBlockForGFO div button {
+    margin-left: 30px;
+}
+
+#productBlock p,
+#productBlock h2{
+    
+    margin-left:  30px;
+}
+
+#productBlock #goodOneImg {
+    padding-top: 30px;
+    width: 500px;
+    height: 500px;
+}
+
+#flexBlockForGFO { 
+    display: flex;
+}
+
 #productBlock a:hover {
-   opacity: 0.8;
+    opacity: 0.8;
 }
 
-.card{
+#subCategories {
+    margin: 0 30px 30px 0
+}
+
+.card {
     display: flex;
     margin-bottom: 30px;
 }
 
-.card div{
+.card div {
     padding: 40px;
     border-bottom: 2px solid black;
     border-right: 2px solid black;
@@ -119,38 +150,38 @@ flexBlock img {
     padding-right: 30px;
 }
 
-.b-container{
-    width:200px;
-    height:150px;
+.b-container {
+    width: 200px;
+    height: 150px;
     background-color: #ccc;
-    margin:0px auto;
-    padding:10px;
-    font-size:30px;
+    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 {
+    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;
+.b-popup .b-popup-content {
+    margin: 40px auto 0px auto;
     width: 1000px;
     min-height: 500px;
-    padding:10px;
+    padding: 10px;
     background-color: yellow;
-    border-radius:5px;
+    border-radius: 5px;
     box-shadow: 0px 0px 10px #000;
 }
 
 .b-popup-content {
     padding: 300px;
     display: flex;
-    
-    flex-direction: column
+
+    flex-direction: column;
 }
 
 #divToCart {
@@ -160,20 +191,19 @@ flexBlock img {
     width: 700px;
 }
 
-#divToCart img{
+#divToCart img {
     max-width: 100px;
     max-height: 100px;
 }
 
 #divToCart img,
-#divToCart h2{
+#divToCart h2 {
     margin-right: 30px;
 }
 #divToCart h2 {
     align-items: center;
 }
 
-
 .b-popup-content button {
     width: 30px;
     height: 30px;
@@ -186,7 +216,7 @@ flexBlock img {
     right: 420px;
 }
 
-#buttonDelete{
+#buttonDelete {
     width: 100px;
     position: absolute;
     top: 70px;
@@ -230,7 +260,6 @@ flexBlock img {
     margin-right: 10px;
 }
 
-
 #pCount {
     border-radius: 50%;
     background-color: red;
@@ -239,21 +268,22 @@ flexBlock img {
     font-size: 14px;
 }
 
+.buyBlock {
+    display: flex;
+    align-items: center;
+    justify-content: end;
+}
 
+.buyBlock button {
+    width: 150px;
+    height: 50px;
+    margin: 0 30px 0 30px;
+}
 
-
-
-
-
-
-
-
-
-
-.footer{
+.footer {
     padding-top: 20px;
-    color:white;
+    color: white;
     min-height: 80px;
     background-color: tomato;
     text-align: center;
-}
+}