diff --git a/basket_script.js b/basket_script.js new file mode 100644 index 0000000..21ca870 --- /dev/null +++ b/basket_script.js @@ -0,0 +1,184 @@ +'use strict'; + +// класс товара в корзине +class BasketItem { + constructor(title, price, quantity, picture) { + this.title = title; + this.price = price; + this.picture = picture; + this.quantity = quantity; + } + + render() { + return `
+ ${this.picture} + ${this.title} + ${this.price} руб + + ${this.quantity*this.price} руб +
+ удалить +
+
`; + } +} + +// класс Корзины товаров +class BasketList { + constructor() { + this.shopCartList = []; + } + + #makeGETRequest(url) { + console.log('Направляю запрос корзины') + return new Promise((resolve, reject) => { + let xhr; + if (window.XMLHttpRequest) { + xhr = new XMLHttpRequest(); + } else if (window .ActiveXObject) { + xhr = new ActiveXObject("Microsoft.XMLHTTP"); + } + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + console.log('корзина получена') + resolve(xhr.response); + } else { + reject(); + } + } + } + + xhr.open( 'GET' , url, true ); + xhr.send(); + }); + } + + #makePOSTRequest(url, data) { + return new Promise((resolve, reject) => { + let xhr; + if (window.XMLHttpRequest) { + xhr = new XMLHttpRequest(); + } else if (window.ActiveXObject) { + xhr = new ActiveXObject("Microsoft.XMLHTTP"); + } + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + resolve(xhr.response); + } else { + reject(); + } + } + } + xhr.open('POST', `/${url}`, true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.send(data); + }); + } + + getShopCartList() { + this.#makeGETRequest(`/shopCartData`) + .then((data) => { + this.shopCartList = JSON.parse(data); + this.render(); + }) + .catch(() => { + console.log('Error: data not found'); + }); + } + + toggle() { + document.querySelector('.basket_container').classList.toggle('hidden'); + } + + showSummary() { + let totalPrice = 0; + let totalQuantity = 0; + + this.shopCartList.forEach(({ price, quantity }) => { + totalPrice += price*quantity; + totalQuantity += quantity; + }); + + document.querySelector('.total_quantity').innerHTML = `${totalQuantity}`; + document.querySelector('.total_price').innerHTML = `${totalPrice}`; + } + + render() { + let html = ''; + + this.shopCartList.forEach(({ title, price, quantity, picture }) => { + const basketItem = new BasketItem(title, price, quantity, picture); + html += basketItem.render(); + }); + + document.querySelector('.basket_list').innerHTML = html; + + this.showSummary(); + + // для инициализации возможности удаления изменения количесвта товара при "раскрытии" корзины + if (this.shopCartList.length > 0) { + let changeShopCart = document.querySelector('.basket_container'); + changeShopCart.addEventListener('click', (event) => { + // console.log('click'); + event.stopImmediatePropagation(); //для предотвращения повторных запросов при ререндеринге корзины + + if (event.target.className == 'product_delete') { + let name = event.target.parentNode.parentNode.childNodes[3].innerText; + const productToDelete = {"title": name}; + this.delete(JSON.stringify(productToDelete)); + } else if (event.target.className == 'change_quantity') { + let newQuantity = +event.target.value; + let name = event.target.parentNode.childNodes[3].innerText; + const productToChange = {"title": name, "quantity": newQuantity}; + + if (newQuantity < 1) { + this.delete(JSON.stringify(productToChange)); + } else { + this.changeQuantity(JSON.stringify(productToChange)); + } + } + }); + } + } + + // добавление товара + add(item) { + this.#makePOSTRequest('addToCart', item) + .then((data) => { + console.log('товар добавлен'); + }) + .catch(() => { + console.log('Error: data not found'); + }); + } + + // удаление товара + delete(item) { + this.#makePOSTRequest('deleteFromCart', item) + .then((data) => { + this.shopCartList = JSON.parse(data); + if (this.shopCartList.length == 0) { + this.shopCartList = []; + } + this.render(); + }) + .catch(() => { + console.log('Error: data not found'); + }); + } + + // изменение количества товара внутри корзины + changeQuantity(item) { + this.#makePOSTRequest('changeQuantity', item) + .then((data) => { + this.shopCartList = JSON.parse(data); + this.render(); + }) + .catch(() => { + console.log('Error: data not found'); + }); + } +} diff --git a/catalog_script.js b/catalog_script.js new file mode 100644 index 0000000..d93e667 --- /dev/null +++ b/catalog_script.js @@ -0,0 +1,33 @@ +'use strict'; + +class ProductsList { + constructor() { + this.products = []; + this.filteredProducts = []; + } + + makeGETRequest(url) { + return new Promise((resolve, reject) => { + let xhr; + if (window.XMLHttpRequest) { + xhr = new XMLHttpRequest(); + } else if (window .ActiveXObject) { + xhr = new ActiveXObject("Microsoft.XMLHTTP"); + } + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + console.log('католог получен') + resolve(xhr.response); + } else { + reject(); + } + } + } + + xhr.open( 'GET' , url, true ); + xhr.send(); + }); + } +} diff --git a/data/cart.json b/data/cart.json new file mode 100644 index 0000000..6fda70d --- /dev/null +++ b/data/cart.json @@ -0,0 +1 @@ +[{"title":"Rollerblade Twister Edge","price":150,"picture":"\"\"","quantity":1}] \ No newline at end of file diff --git a/responses/catalog.json b/data/catalog.json similarity index 90% rename from responses/catalog.json rename to data/catalog.json index 53675f7..444b51b 100644 --- a/responses/catalog.json +++ b/data/catalog.json @@ -12,7 +12,7 @@ "image": "image/k2_alexis_80_pro.jpg" }, { - "title": "FRX 80", + "title": "Seba FRX 80", "price": 350, "link": "seba_frx_80", "image": "image/seba_frx_80.jpg" diff --git a/image/heder.jpg b/image/heder.jpg new file mode 100644 index 0000000..69bc24b Binary files /dev/null and b/image/heder.jpg differ diff --git a/image/k2_alexis_80_pro.jpg b/image/k2_alexis_80_pro.jpg new file mode 100644 index 0000000..30dfcc4 Binary files /dev/null and b/image/k2_alexis_80_pro.jpg differ diff --git a/image/logo_2.png b/image/logo_2.png new file mode 100644 index 0000000..e17b23b Binary files /dev/null and b/image/logo_2.png differ diff --git a/image/rollerblade_macroblade_84W.jpg b/image/rollerblade_macroblade_84W.jpg new file mode 100644 index 0000000..3440093 Binary files /dev/null and b/image/rollerblade_macroblade_84W.jpg differ diff --git a/image/seba_frx_80.jpg b/image/seba_frx_80.jpg new file mode 100644 index 0000000..687ea1a Binary files /dev/null and b/image/seba_frx_80.jpg differ diff --git a/image/twister_edge.jpg b/image/twister_edge.jpg new file mode 100644 index 0000000..f6b0492 Binary files /dev/null and b/image/twister_edge.jpg differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..37ebf68 --- /dev/null +++ b/index.html @@ -0,0 +1,51 @@ + + + + + Rollerskates + + + + +
+
+ + +
+
+

Роликовые коньки

+
+ +
+ + + + + + +
+
+ + + + + + \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..fa80411 --- /dev/null +++ b/main.js @@ -0,0 +1,131 @@ +'use strict'; + +window.addEventListener('load', () => { + const catalogList = new ProductsList(); + const shopingCart = new BasketList(); + + Vue.component('cart', { + template: ` +
+ Корзина +
+ `, + methods: { + showCart() { + shopingCart.getShopCartList(); + shopingCart.toggle(); + } + } + }); + + Vue.component('search-field', { + props: ['searchName'], + template: ` + + `, + methods: { + save() { + this.$emit("change", this.searchName); + } + } + }); + + Vue.component('catalog-list', { + props: ['products'], + template: ` +
+ +
+ ` + }); + + Vue.component('products-item', { + props: ['product'], + template: ` +
+
+ + + +

+ {{ product.title }} +

{{ product.price }} руб.

+ +
+ `, + methods: { + addToCart(event) { + let name = event.target.parentNode.childNodes[3].innerText; + let price = +event.target.parentNode.childNodes[5].childNodes[0].innerText; + let image = event.target.parentNode.childNodes[0].childNodes[0].childNodes[0].outerHTML; + let item = {"title": name, "price": price, "picture": image}; + + shopingCart.add(JSON.stringify(item)); + } + } + }); + + Vue.component('basket-list', { + template: ` + + `, + methods: { + closeBasket() { + shopingCart.toggle(); + } + } + }); + + new Vue({ + el: '.wraper', + data: { + products: [], + filteredProducts: [], + productName: '', + }, + methods: { + getProducts(list) { + catalogList.makeGETRequest(`/catalogData`) + .then((data) => { + this.products = JSON.parse(data); + this.filteredProducts = JSON.parse(data); + }) + .catch(() => { + console.log('Error: data not found'); + }); + }, + filter(name) { + this.productName = name; + console.log(this.productName); + const regexp = new RegExp(this.productName, 'i'); + this.filteredProducts = this.products.filter(product => regexp.test(product.title)); + } + }, + mounted() { + this.getProducts(); + } + }); + +}); \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..a363893 --- /dev/null +++ b/server.js @@ -0,0 +1,104 @@ +const express = require('express'); +// const bodyParser = require('body-parser'); +const fs = require('fs'); +// console.log(fs); + +const app = express(); +const jsonParser = express.json(); +// app.use(bodyParser.json()); + +app.listen(3000, () => { +console.log('server is running on port 3000!'); +}); + +app.use(express.static('.')); + +app.get('/catalogData', (req, res) => { + fs.readFile('data/catalog.json', 'utf8', (err, data) => { + res.send(data); + }); +}); + +app.get('/shopCartData', (req, res) => { + fs.readFile('data/cart.json', 'utf8', (err, data) => { + res.send(data); + }); +}); + +app.post('/addToCart', jsonParser, (req, res) => { + fs.readFile('data/cart.json', 'utf8', (err, data) => { + const cart = JSON.parse(data); + const item = req.body; + + // составляем список товаров из корзины + let productsInCart = []; + cart.forEach(({ title }) => productsInCart.push(title)); + // если товар уже есть в корзине увеличить количество + if (productsInCart.includes(item.title)) { + for (let i = 0; i < cart.length; i++) { + if (cart[i].title == item.title) { + cart[i].quantity += 1; + break; + } + } + // иначе добавить в корзину + } else { + item.quantity = 1; + cart.push(item); + } + + fs.writeFile('data/cart.json', JSON.stringify(cart), (err) => { + if (err) { + res.send('{"result": 0}'); + } else { + res.send('{"result": 1}'); + } + }); + }); +}); + +app.post('/deleteFromCart', jsonParser, (req, res) => { + fs.readFile('data/cart.json', 'utf8', (err, data) => { + const cart = JSON.parse(data); + const item = req.body; + console.log('запрос к серверу'); + for (let i = 0; i < cart.length; i++) { + if (cart[i].title == item.title) { + cart.splice(i, 1); + break; + } + } + + fs.writeFile('data/cart.json', JSON.stringify(cart), (err) => { + if (err) { + res.send('{"result": 0}'); + } else { + res.send(JSON.stringify(cart)); + } + }); + }); +}); + + + +app.post('/changeQuantity', jsonParser, (req, res) => { + fs.readFile('data/cart.json', 'utf8', (err, data) => { + const cart = JSON.parse(data); + const item = req.body; + + for (let i = 0; i < cart.length; i++) { + if (cart[i].title == item.title) { + cart[i].quantity = item.quantity; + break; + } + } + + fs.writeFile('data/cart.json', JSON.stringify(cart), (err) => { + if (err) { + res.send('{"result": 0}'); + } else { + res.send(JSON.stringify(cart)); + } + }); + }); +}); diff --git a/style.css b/style.css new file mode 100644 index 0000000..7c60a0d --- /dev/null +++ b/style.css @@ -0,0 +1,517 @@ +body { + background: #f8f8f8; +} + +p { + color: #484343; + font-size: 16px; + font-weight: 400; + text-align: left; + line-height: 24px; + font-family: Tahoma, Geneva, sans-serif; +} + +h1 { + font-family: Tahoma, Geneva, sans-serif; + font-style: italic; +} + +h3 { + font-family: ‘Times New Roman’, Times, serif; +} + +a { + font-family: Tahoma, Geneva, sans-serif; +} + +hr { + clear: both; +} + +.buy-button { + background: darkorange; + border-radius: 10px; + min-width: 80px; + min-height: 40px; + font-family: Tahoma, Geneva, sans-serif; + font-size: 16px; + border: none; + color: #fff; + margin-bottom: 3px; + padding-left: 10px; + padding-right: 10px; +} + +.buy-button:hover { + background: orange; +} + +.buy-button:focus { + box-shadow: 0 0 0 1px gray; + outline: none; + +} + +iframe { + height: 374px; + width: 100%; +} + +.wraper { + min-height: 100%; + max-width: 1200px; + position: relative; + margin: 0 auto; +} + +.main-wall { + background-image: url(image/s1200.jfif); + background-repeat: no-repeat; + min-height: 801px; + position: relative; +} + +.hidden { + display: none; +} + +.picture { + outline: 1px solid #52dbd3; + float: left; +} + +.short { + overflow: hidden; + padding-left: 10px; +} + +.description { + clear: both; + padding-top: 1px; +} + +.picture:hover { + outline: none; +} + +.container { + width: 100%; + margin: 0 auto; + position: relative; +} + +.main-page { + float: right; + margin-right: 20px; +} + +.main-page-text { + color: #fff; +} + +.header { + position: relative; + height: 100px; + margin: 0 auto; + background-image: url(image/heder.jpg); + min-width: 600px; +} + +#logo { + margin: 11px 0 0 30px; + width: 156px; + height: 78px; + display: inline-block; + vertical-align: bottom; + position: absolute; +} + +.main_menu { + text-align: right; + position: absolute; + min-width: 414px; + min-height: 90px; + right: 0; +} + +.menulist { + font-family: Tahoma, Geneva, sans-serif; + font-weight: 900; + font-size: 1.3em; + display: inline-block; + margin-top: 35px; +} + +.menulist li { + margin-right: 25px; + display: inline; +} + +.menulist li:last-child { + margin-right: 125px; +} + +.menulist li a { + color: silver; + text-decoration: none; +} + +.menulist li a:hover { + color: #52dbd3; + /*text-decoration: overline;*/ + background: inherit; +} + +.cart { + position: absolute; + display: inline-block; + right: 5px; + bottom: 0; +} + +.cart-button { + display: inline-block; + float: right; + color: #fff; + font-size: 10px; + font-weight: 700; + line-height: 20px; + text-transform: uppercase; + letter-spacing: 2px; + width: 92px; + height: 20px; + box-shadow: 0 2px 5px 1px rgba(109, 109, 109, 0.35); + border-radius: 23px; + text-align: center; + text-decoration: none; + position: relative; + border: 1px solid #ffffff; +} + +.cart-button:hover { + background-color: #ffffff; + color: #212121; +} + +.cart-button:active { + box-shadow: none; + border: none; +} + +.catalog a:hover { + background: #eaeaea; +} + +.features { + color: #484343; + font-size: 16px; + font-weight: 400; + font-family: Tahoma, Geneva, sans-serif; + line-height: 20px; +} + +.heading { + color: #000; + font-size: 18px; + font-weight: 400; + background: #eaeaea; +} + +#short { + color: #707070; + font-size: 14px; + font-style: italic; + line-height: 16px; +} + +.user { + color: blue; + font-size: 14px; + font-family: ‘Courier New’, Courier, monospace; + text-transform: capitalize; +} + +#email:focus { + color: yellow; + background: gray; +} + +.product:first-letter { + font-family: ‘Courier New’, Courier, monospace; /* Шрифт первой буквы */ + font-size: 200%; /* Размер шрифта первого символа */ + color: orange; +} + +.catalog { + text-align: center; +} + +.product_preview { + display: inline-block; + width: 200px; + text-align: center; + vertical-align: top; + margin-left: 20px; +} + +.preview_image { + display: inline-block; + width: 120px; + height: 120px; +} + +.product_preview p { + text-align: center; +} + +.catalog div:first-child { + margin-left: 0; +} + +.catalog img { + outline: 1px solid silver; +} + +.prod a { + height: 280px; + width: 200px; +} + +.catalog-menu { + text-align: center; + font-family: Tahoma, Geneva, sans-serif; + margin-bottom: 40px; +} + +.clearfix { + clear: both; +} + +.search { + text-align: right; + color: #484343; + font-size: 12px; + font-weight: 400; + font-family: Tahoma, Geneva, sans-serif; +} + +.search input { + /*float: right;*/ + margin-bottom: 20px; + width: 120px; +} + +.search-button { + color: #484343; + /*min-height: 15px;*/ +} + +.catalog-menu-list { + display: inline; + margin-right: 25px; + font-family: Tahoma, Geneva, sans-serif; + padding: 5px; +} + +.catalog-active { + /*border-top: 2px solid #000;*/ + border-bottom: 2px solid #52dbd3; +} + +.catalog-active a { + color: #000; +} + +.catalog-link { + font-family: ‘Times New Roman’, sans-serif; + text-decoration: none; + color: grey; + font-weight: 700; +} + + +.catalog-menu-list a:hover { + color: #000; +} + +/* корзина */ + +.basket_container { + position: absolute; + top: 105px; + right: 0; + width: 100%; + height: 100%; + background-color: #f8f8f8; +} + +.close { + position: absolute; + right: 25px; + color: darkblue; + border: 2px solid darkblue; + border-radius: 20%; + padding: 1px 5px; +} + +.close:hover { + color: red; + border: 2px solid red; +} + +.basket_container .head { + margin: 25px auto; +} + +.h2 { + margin-top: 10px; +} + +.basket_list { + position: relative; +} + +.basket_record { + width: 60%; + min-width: 670px; + display: block; + margin: 10px auto; + border-bottom: 1px solid grey; + position: relative; +} + +.basket_list .basket_record { + position: relative; + display: block; + height: 170px; + float: none; +} + +.basket_list span.product_name { + margin-left: 1em; + width: 8em; + position: relative; + display: block; + float: left; +} + +.basket_list span.product_price { + margin-left: 1em; + width: 7em; + position: relative; + display: block; + float: left; +} + +.basket_list span.product_cost { + margin-left: 1em; + width: 7em; + position: relative; + display: block; + float: left; +} + +.basket_delete { + display: inline-block; + /*padding: 10px 0;*/ +} + +.basket_delete a { + text-decoration: none; + color: #000; +} + +.basket_delete a:hover { + text-decoration: none; + color: red; +} + +.basket_summary p { + color: #484343; + font-size: 20px; + font-weight: 900; + text-align: left; + line-height: 24px; + font-family: Tahoma, Geneva, sans-serif; + margin-top: 20px; + margin-left: 20px; +} + +.basket_list input[type="number"] { + width: 3em; + position: relative; + display: block; + float: left; +} + +.basket_list .basket_record button { + margin-top: 50px; + margin-left: 390px; + width: 150px; + position: absolute; + display: block; + float: left; +} + +.basket_list button { + margin-left: 45px; + width: 150px; + display: block; + float: left; +} + +.basket_container button a { + text-decoration: none; + color: #555; +} + +.basket_container img { + display: block; + float: left; + width: 120px; + border-radius: 25px; +} + +.basket_container input[type="submit"] { + width: 150px; + display: block; + float: left; + margin-bottom: 25px; +} + +.basket_container .basket_summary { + display: block; + margin-left: 50px; + margin-bottom: 15px; + color: #555; +} + +.make_order { + margin-left: 50px; +} + + +/*Далее код для прижатия "подвала" к нижней части страницы*/ +html, body { + height: 100% +} + +.forfooter { + height: 60px; + max-width: 1200px; +} + +.footer { + height: 60px; + background-color: #eaeaea; + margin-top: -60px; + max-width: 1200px; + margin: -60px auto 0 auto; +} + +.footer p { + text-align: right; + margin-right: 90px; + margin-top: 10px; +} +