Browse Source

HW_module done

AntonPyvovarov 1 year ago
parent
commit
9d194dc2b6

+ 9 - 11
HW15/main.js

@@ -1,11 +1,5 @@
 
-const originalFetch = fetch;
-fetch = (url, params = { headers: {} }) => {
-    params.headers.Authorization = "Bearer " + localStorage.authToken
-    return originalFetch(url, params)
-};
-
-const graphiQL = "http://shop-roles.node.ed.asmer.org.ua/graphql";
+const backURL = "http://shop-roles.node.ed.asmer.org.ua/graphql";
 
 let gql = (url, query, variables = {}) =>
     fetch(url, {
@@ -19,7 +13,7 @@ let gql = (url, query, variables = {}) =>
 
 const categoryFind = () =>
     gql(
-        graphiQL,
+        backURL,
         `
             query catz{
                 CategoryFind(query:"[{}]") {
@@ -31,7 +25,7 @@ const categoryFind = () =>
 
 const registerUser = (login, password) =>
     gql(
-        graphiQL,
+        backURL,
         `
             mutation register($login:String, $password:String){
                 UserUpsert(user: {login:$login, password:$password}){
@@ -44,7 +38,7 @@ const registerUser = (login, password) =>
 
 const login = (login, password) =>
     gql(
-        graphiQL,
+        backURL,
         `
             query login($login:String, $password:String){
                 login(login:$login, password:$password)
@@ -55,7 +49,7 @@ const login = (login, password) =>
 
 const categories = () =>
     gql(
-        graphiQL,
+        backURL,
         `
                 query categoryFind{
                     CategoryFind(query:"[{}]"){
@@ -85,6 +79,10 @@ const categories = () =>
         "category by id with products:",
         await categories()
     );
+    console.log(
+        "product search by id:",
+        await productSearchByIdImgDiscr("62c9472cb74e1f5f2ec1a0d3")
+    );
 })();
 
 

BIN
HW16,17_module/img/basket.png


BIN
HW16,17_module/img/nerozetka.jpg


BIN
HW16,17_module/img/user.png


+ 36 - 0
HW16,17_module/index.html

@@ -0,0 +1,36 @@
+<!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">
+        <link rel="stylesheet" href="style.css">
+        <title>NotРОЗЕТКА</title>
+    </head>
+    <body>
+        <header>
+            <a class="logo" href="#">
+                <img src="img/nerozetka.jpg" alt="./nerozetka.jpg">
+            </a>
+            <div id="logo">
+                <h1>Добро пожаловать в интернет магазин</h1>
+            </div>
+            <div id='login'>
+                <span class="loginQ">?</span><a href="#/login"><img src="img/user.png"></a>
+            </div>
+
+            <div id='cartIcon'>
+                <span class="cartN">0</span><a href="#/cart"><img src="img/basket.png"></a>
+            </div>
+        </header>
+        <div id='mainContainer'>
+            <aside id='aside'>
+                Категории
+            </aside>
+            <main id='main'>
+                Контент
+            </main>
+        </div>
+        <script src='main.js'></script>
+    </body>
+</html>

+ 645 - 0
HW16,17_module/main.js

@@ -0,0 +1,645 @@
+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}){
+    if (type === 'PROMISE'){
+        return {
+            ...state,
+            [name]:{status, payload, error}
+        }
+    }
+    return state
+}
+
+const jwtDecode = token => {
+    try {
+        let [,payload] = token.split(".");
+        let payloadParsed= JSON.parse(atob(payload));
+        return payloadParsed;
+    }
+    catch(e){
+        console.log(e);
+    }
+}
+
+function authReducer(state, {type, token}) {
+    if (state === undefined) {
+        if(localStorage.authToken) {
+            type = "AUTH_LOGIN";
+            token = localStorage.authToken;
+        } else {
+            return {};
+        }
+    }
+    if (type === 'AUTH_LOGIN') {
+        let payload = jwtDecode(token);
+        if (payload) {
+            localStorage.authToken = token;
+            return {token, payload};
+        }
+    }
+    if (type === 'AUTH_LOGOUT') {
+        localStorage.authToken="";
+        return {};
+    }
+    return state;
+}
+
+function combineReducers(reducers) {
+    return (state = {}, action) => {
+        const newState = {}
+        for (const [reducerName, reducer] of Object.entries(reducers)) {
+            let newSubState = reducer(state[reducerName], action)
+
+            if (newSubState !== state[reducerName]) {
+                newState[reducerName] = newSubState
+            }
+        }
+
+        if (Object.keys(newState).length !== 0) {
+            return { ...state, ...newState }
+        } else {
+            return state
+        }
+    }
+}
+
+function cartReducer(state={}, {type, good={}, count=1}) {
+//каков state
+//{
+//  _id1: {count:1, good: {_id1, name, price, images}}
+//  _id2: {count:1, good: {_id1, name, prica, images}}
+//}
+//
+const {_id} = good;
+    if (type==='CART_ADD') {
+        return {
+            ...state,
+            [_id]:{count: (state[_id]? (state[_id].count+=count) : count), good: good}
+        }
+    }
+    if (type==='CART_CHANGE') {
+        return {
+            ...state,
+            [_id]:{count: count, good: good}
+        }
+    }
+    if (type==='CART_DELETE') {
+        let newState = {... state};
+        delete newState[_id];
+        // const {_id, ...newState} = state; 
+        return {
+            ...newState, 
+        }
+    }
+    if (type==='CART_CLEAR') {
+        return {}
+    }
+
+
+    return state;
+}
+
+const store = createStore(combineReducers({promise: promiseReducer,
+    auth: authReducer, cart: cartReducer}));
+
+
+const actionCartAdd = (good, count=1)       => ({type: 'CART_ADD', good, count});
+const actionCartChange = (good, count=1)    => ({type: 'CART_CHANGE', good, count});
+const actionCartDelete = (good)             => ({type: 'CART_DELETE', good});
+const actionCartClear = ()                 => ({type: 'CART_CLEAR'});
+
+const cartEl = document.getElementsByClassName("cartN")[0];
+let username;
+const loginEl = document.getElementsByClassName("loginQ")[0];
+const logoEl = document.getElementById("logo");
+
+// const store = createStore(promiseReducer) //не забудьте combineReducers если он у вас уже есть
+
+store.subscribe(() => console.log(store.getState()));
+
+
+const actionPending             = name => ({type:'PROMISE',name, status: 'PENDING'});
+const actionFulfilled = (name,payload) => ({type:'PROMISE',name, status: 'FULFILLED', payload});
+const actionRejected  = (name,error)   => ({type:'PROMISE',name, status: 'REJECTED', error});
+const actionAuthLogin = (token)        => ({ type: 'AUTH_LOGIN', token });
+const actionAuthLogout = ()            => ({ type: 'AUTH_LOGOUT' });
+const actionPromise = (name, promise) =>
+    async dispatch => {
+        dispatch(actionPending(name))
+        try {
+            let payload = await promise
+            dispatch(actionFulfilled(name, payload))
+            return payload
+        }
+        catch(error){
+            dispatch(actionRejected(name, 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 backendURL = "http://shop-roles.node.ed.asmer.org.ua";
+
+const gql = getGQL(backendURL + '/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){
+          subCategories{
+            name _id goods {
+              _id
+             name
+            }
+           }
+            _id name goods {
+                _id name price images {
+                    url
+                }
+            }
+        }
+    }`, { q: JSON.stringify([{ _id }]) }));
+
+
+const actionGoodById = (_id) =>
+    actionPromise('goodById', gql(`query goodz($q: String){
+        GoodFindOne(query: $q) {
+        _id name price description
+        images{
+            url
+        }
+        categories{
+            name
+        }
+        }
+    }`, {q: JSON.stringify([{_id}])}));
+
+
+const orderHistory = () =>
+    actionPromise(
+      'history',
+      gql(` query OrderFind{
+        OrderFind(query:"[{}]"){
+            _id total createdAt orderGoods{
+                count good{
+                    _id name price images{
+                      url
+                    }
+                }
+                owner{
+                    _id login 
+                }
+            }
+        }
+    }
+  `));
+
+  const actionLogin = (login, password) =>
+  actionPromise(
+    'login',
+    gql(
+      `query log($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));
+  console.log(token);
+  if (token) {
+    dispatch(actionAuthLogin(token));
+  }
+  alerting();
+};
+
+const actionRegister = (login, password) =>
+  actionPromise(
+    'register',
+    gql(
+      `mutation register ($login:String, $password:String){
+  UserUpsert(user:{login:$login, password:$password}){
+	_id login
+ }
+}`,
+      { 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 actionFullRegister = (login, password) => async (dispatch) => {
+  let id = await dispatch(actionRegister(login, password));
+//   console.log(id);
+  alertingReg(id, login);
+  if (id) {
+    dispatch(actionFullLogin(login, password));
+  }
+};
+
+store.dispatch(actionRootCats());
+
+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 = {
+        category(){
+            store.dispatch(actionCatById(_id));
+        },
+        good(){
+            // console.log("good")
+            //задиспатчить actionGoodById
+            // console.log('ТОВАРОСТРАНИЦА')
+            store.dispatch(actionGoodById(_id));
+        },
+        login(){
+            main.innerHTML = `<h2>Для осуществления заказов:<br>если вы зарегистрированы,
+            введите логин и пароль и нажмите Войти;<br>если не зарегистрированы - нужно зарегистрироваться</h2>`;
+            const formEl = document.createElement("div");
+            const p1el = document.createElement("p");
+            const p2el = document.createElement("p");
+            const p3el = document.createElement("p");
+            const br1El = document.createElement("br");
+            const br2El = document.createElement("br");
+            const br3El = document.createElement("br");
+            const input1El = document.createElement("input");
+            const input2El = document.createElement("input");
+            const buttonLogEl = document.createElement("button");
+            const buttonRegEl = document.createElement("button");
+            const buttonLogOutEl = document.createElement("button");
+            p1el.innerText = "Введите логин:";
+            p2el.innerText = "Введите пароль:";
+            input1El.id = "inputLogin";
+            input2El.id = "inputPassword";
+            input2El.type = "password";
+            buttonLogEl.innerText = "Войти";
+            buttonRegEl.innerText = "Зарегистрироваться";
+            buttonLogOutEl.innerText = "Выйти";
+            if(username) {
+                buttonLogEl.disabled = true;
+                buttonRegEl.disabled = true;
+            }
+            buttonLogEl.onclick = () => {
+                store.dispatch(actionFullLogin(input1El.value, input2El.value));
+                // if (username) location.hash = "#/goods";
+            }
+            buttonRegEl.onclick = () => {
+                store.dispatch(actionFullRegister(input1El.value, input2El.value));
+            }
+            buttonLogOutEl.onclick = () => {
+                store.dispatch(actionAuthLogout());
+                username = "";
+                document.location.hash = "#/login/";
+            }
+            formEl.append(p1el,input1El,p2el,input2El,br1El,br2El,buttonLogEl,buttonLogOutEl,buttonRegEl);
+            main.append(formEl);
+            //по кнопке - store.dispatch(actionFullLogin(login, password))
+        },
+        register(){
+            //отрисовка тут
+            //по кнопке - store.dispatch(actionFullRegister(login, password))
+        },
+        dashboard(){
+            store.dispatch(orderHistory());
+
+        },
+        cart(){
+            const cart = store.getState().cart;
+            if(Object.entries(cart).length>0) {
+                main.innerHTML = `<h2>Содержимое вашей корзины</h2>`;
+                const {cart} = store.getState();
+                let totalScore = 0;
+                let totalCount = 0;
+                const tableCartEl = document.createElement("table");
+                const trEl = document.createElement("tr");
+                const td1El = document.createElement("td");
+                const td2El = document.createElement("td");
+                const td3El = document.createElement("td");
+                const td4El = document.createElement("td");
+                const td5El = document.createElement("td");
+                td1El.innerText = "Удалить";
+                td2El.innerText = "Наименование товара";
+                td3El.innerText = "Цена";
+                td4El.innerText = "Количество";
+                td5El.innerText = "Изменить";
+                trEl.append(td1El,td2El,td3El,td4El,td5El);
+                tableCartEl.append(trEl);
+    
+                if (Object.entries(cart).length>0) {
+                    for (let [key, value] of Object.entries(cart)) {
+                        const {count, good} = value;
+                        const {name, _id, price} = good;
+                        const buttonDel = document.createElement("button");
+                        const buttonMinus = document.createElement("button");
+                        const buttonPlus = document.createElement("button");
+                        buttonMinus.innerText = "-";
+                        buttonPlus.innerText = "+";
+                        buttonDel.innerText = "Удалить";
+
+
+                        buttonMinus.onclick = () => {
+                            if(count==1) {
+                                store.dispatch(actionCartDelete({_id, name, price}));
+                            } else {
+                            store.dispatch(actionCartChange({_id, name, price}, count-1));
+                            }
+                            document.location.hash = `#/cart/${_id}/${count}`
+                        }
+                        buttonPlus.onclick = () => {
+                            store.dispatch(actionCartChange({_id, name, price}, count+1));
+                            document.location.hash = `#/cart/${_id}/${count}`
+                        }
+                        buttonDel.onclick = () => {
+                            store.dispatch(actionCartDelete({_id, name, price}));
+                            document.location.hash = `#/cart/${_id}`;
+                        }
+    
+                        const trEl = document.createElement("tr");
+                        const td1El = document.createElement("td");
+                        const td2El = document.createElement("td");
+                        const td3El = document.createElement("td");
+                        const td4El = document.createElement("td");
+                        const td5El = document.createElement("td");
+                        td1El.append(buttonDel);
+                        td2El.innerText = name;
+                        td3El.innerText = price;
+                        td4El.innerText = count;
+                        td5El.append(buttonMinus,buttonPlus);
+                        trEl.append(td1El,td2El,td3El,td4El,td5El);
+                        tableCartEl.append(trEl);
+    
+                        totalScore+=price*count;
+                        totalCount+=count;
+                        
+                    }
+                    const trEl = document.createElement("tr");
+                    const td1El = document.createElement("td");
+                    const td2El = document.createElement("td");
+                    const td3El = document.createElement("td");
+                    const td4El = document.createElement("td");
+                    const td5El = document.createElement("td");
+                    td1El.innerText = "";
+                    td2El.innerText = "";
+                    td3El.innerText = totalScore;
+                    td4El.innerText = totalCount;
+                    td5El.innerText = "";
+                    trEl.append(td1El,td2El,td3El,td4El,td5El);
+                    tableCartEl.append(trEl);
+                
+                }
+                
+                main.append(tableCartEl);
+    
+                const buttonCartClear = document.createElement("button");
+                const buttonSend = document.createElement("button");
+                const buttonHistory = document.createElement("button");
+                buttonSend.innerText = "Отправить заказ";
+                buttonCartClear.innerText = "Очистить корзину";
+                buttonHistory.innerText = "История заказов";
+                buttonSend.onclick = () => {
+                    store.dispatch(actionOrder());
+                    
+                }
+                buttonCartClear.onclick = () => {
+                    store.dispatch(actionCartClear());
+                    document.location.hash = "#/cart/";
+                }
+                buttonHistory.onclick = () => {
+                    document.location.hash = "#/dashboard";
+                }
+
+                main.append(buttonSend,buttonCartClear, buttonHistory);
+            } else {
+                main.innerHTML = `<h2>Ваша корзина пуста...</h2><a href="#/dashboard">история заказов</a>`;
+            }
+
+        },
+    }
+    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} = catById.payload 
+        main.innerHTML = `<h1>${name}</h1>`
+
+    if (catById.payload.subCategories) {
+        for (const {_id, name} of catById.payload.subCategories){
+            const card      = document.createElement('div')
+            card.innerHTML = `<a href="#/category/${_id}"><h2>${name}</h2></a>`;
+            main.append(card);
+        }
+    }
+    for (const {_id, name, price, images} of catById.payload.goods){
+        const card      = document.createElement('div')
+        card.innerHTML = `<h2>${name}</h2>
+                            <img src="${backendURL}/${images[0].url}" /><br>
+                            <strong>Цена: ${price} грн</strong><br>
+                            <button onclick="store.dispatch(actionCartAdd({_id: '${_id}',
+                                        name: '${name}', price: '${price}'}))">Купить</button>
+                            <a href="#/good/${_id}">описание товара</a>`
+        main.append(card)
+    }
+
+    }
+})
+
+store.subscribe(() => {
+const {goodById} = store.getState().promise;
+const [,route, _id] = location.hash.split('/');
+    //ТУТ ДОЛЖНА БЫТЬ ПРОВЕРКА НА НАЛИЧИЕ goodById в редакс
+    //и проверка на то, что сейчас в адресной строке адрес ВИДА #/good/АЙДИ
+    //в таком случае очищаем main и рисуем информацию про товар с подробностями
+    if (goodById?.payload && route === 'good'){
+        const {_id, name, price, description, images} = goodById.payload; 
+        main.innerHTML = `<h2>${name}</h2>`;
+        const card      = document.createElement('div');
+        card.innerHTML = `<img src="${backendURL}/${images[0].url}" /><br>
+                              <strong>Цена: ${price} грн</strong><br>
+                              <button onclick="store.dispatch(actionCartAdd({_id: '${_id}',
+                                            name: '${name}', price: '${price}'}))">Купить</button>
+                                <p>Описание товара:</p>
+                                <p>${description}</p>
+                                `
+        main.append(card);
+    }
+})
+
+store.subscribe(() => {
+const {cart} = store.getState();
+// console.log(cart);
+let numInCart = 0;
+if (Object.entries(cart).length>0) {
+    for (let [key, value] of Object.entries(cart)) {
+        const {count} = value;
+        numInCart+=count;
+    }
+}
+cartEl.innerText=numInCart;
+    //достаем всю корзину
+    //считаем общее количество всех позиций (3 айфона + 7 пицц = 10)
+    //выводим число в кошик на странице через его HTML id
+});
+
+store.subscribe(() => {
+    const {auth} = store.getState();
+    // const [,route,] = location.hash.split('/');
+    if(Object.keys(auth).length!==0) {
+        username = auth?.payload.sub.login;
+    }
+    if(localStorage.authToken) {
+        // console.log("login");
+        loginEl.innerText = "";
+        logoEl.innerHTML = `\n                <h1>${username}, добро пожаловать в интернет магазин NotРОЗЕТКА</h1>\n            `
+    } else {
+        // console.log("logout");
+        loginEl.innerText = "?";
+        logoEl.innerHTML = `\n                <h1>Добро пожаловать в интернет магазин</h1>\n            `  
+    }
+});
+
+//store.dispatch(actionPromise('delay1000', delay(1000)))
+//store.dispatch(actionPromise('delay2000', delay(2000)))
+//store.dispatch(actionPromise('failedfetch', fetch('https://swapi.dev/api/people/1/')
+  //.then(res => res.json())))
+
+store.subscribe(() => {
+    const {history} = store.getState().promise;
+    const [,route] = location.hash.split('/');
+
+    if (history?.payload && route === 'dashboard'){  
+
+        main.innerHTML = "<p><b>История ваших заказов:</b></p>";
+        const card      = document.createElement('div'); 
+        
+        for(let [key,value] of Object.entries(history.payload)) {
+            const {_id, createdAt, total, orderGoods} = value;
+            const p1El = document.createElement("p");
+            const dateOfOrder = new Date(+createdAt);
+            p1El.innerHTML = `${dateOfOrder.toLocaleDateString()} ${dateOfOrder.toLocaleTimeString()}
+                            Заказ ID: ${_id} от , c ${orderGoods.length} товарами на сумму ${total} грн`;
+            card.append(p1El); 
+        }
+        
+        if (Object.keys(history.payload).length==0) {
+            const p1El = document.createElement("p");
+            p1El.innerHTML = "<p>Вы раньше еще ничего не заказывали</p>";
+            card.append(p1El);
+        }
+        main.append(card);
+
+    }
+    
+});
+
+function alerting() {
+    const {auth} = store.getState();
+    if(Object.keys(auth).length!==0) {
+        username = auth?.payload.sub.login;
+    }
+    if(username) {
+        alert(`${username}, вы успешно зашли на сайт!`);
+        document.location.hash = "#/category/5dc49f4d5df9d670df48cc64";
+        
+    } else {
+        alert("Вы ввели неправильно логин или пароль!");
+    }
+}
+
+function alertingReg(id, login) {
+    if(id) {
+        alert(`Вы успешно зарегистрировались с логином ${login}`);
+    } else {
+        alert(`Логин ${login} уже занят, придумайте другой!`);
+    }
+}

+ 106 - 0
HW16,17_module/style.css

@@ -0,0 +1,106 @@
+* {
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+}
+
+body {
+    background-color: azure;
+}
+
+#mainContainer {
+    padding: 10px;
+    display: flex;
+}
+
+#aside {
+    width: 350px;
+}
+
+#aside > a{
+    color: black;
+    font-size: 25px;
+    text-decoration: none;
+    display: block;
+}
+
+#aside > a:hover {
+    color: crimson;
+}
+
+#login,
+#cartIcon {
+    padding: 5px;
+
+}
+
+#login:hover, #cartIcon:hover {
+    background-color: azure;
+    transition: all 0.5s ease;
+    border-radius: 10px;
+}
+#logo {
+    width: 70%;
+}
+
+.logo img {
+    margin: 5px;
+    width: 70px;
+    height: 70px;
+}
+
+.logo img:hover {
+    scale: 1.2;
+}
+
+header {
+    height: 100px;
+    position: sticky;
+    padding: 10px;
+    top: 0;
+    right: 0;
+    display: flex;
+    justify-content: space-between;
+    background-color:rgb(49, 184, 184);
+}
+
+h1 {
+    margin-left: 10px;
+}
+
+h1::first-letter {
+    text-transform: capitalize;
+}
+
+td {
+    border: 2px solid black;
+    padding: 5px;
+    text-align: center;
+}
+
+#main img {
+    max-width: 300px;
+    max-height: 300px;
+}
+
+button {
+    cursor: pointer;
+    text-align:center;
+    font-size:13px;
+    text-decoration: none;
+    font-weight: 700;
+    padding: 3px 6px;
+    background: #eaeef1;
+    display:block;
+    min-width:100px;
+    background-image: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,.1));
+    border-radius: 3px;
+    color: rgba(0,0,0,.6);
+    text-shadow: 0 1px 1px rgba(255,255,255,.7);
+    box-shadow: 0 0 0 1px rgba(0,0,0,.2), 0 1px 2px rgba(0,0,0,.2), inset 0 1px 2px rgba(255,255,255,.7);
+}
+
+button:hover, button.hover {
+    background: white;
+}
+