product display
Former-commit-id: 860d840458933eae6910fee505d849655d88ffbc
This commit is contained in:
@@ -41,7 +41,11 @@
|
||||
}
|
||||
|
||||
.product-display {
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.product-info {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
@@ -56,7 +60,8 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
@@ -66,15 +71,42 @@
|
||||
margin-block-end: 0.83em;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
.product-tags {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-block-start: 0.83em;
|
||||
margin-block-end: 0.83em;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 0.3em 1em;
|
||||
margin-right: 1em;
|
||||
line-height: 1.5em;
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
background-color: #F2CA52;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.product-listing-price {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.product-listing-price-full {
|
||||
text-decoration: line-through;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.product-listing-price-new {
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.product-description {
|
||||
font-size: 1.5em;
|
||||
font-size: 1em;
|
||||
margin-block-start: 0.83em;
|
||||
margin-block-end: 0.83em;
|
||||
}
|
||||
@@ -88,9 +120,18 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.add-to-cart-button {
|
||||
.product-add-to-basket {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.add-to-basket-button {
|
||||
margin-block-start: 0.83em;
|
||||
margin-block-end: 0.83em;
|
||||
margin-right: 1em;
|
||||
cursor: pointer;
|
||||
background-color: #1A1A1A;
|
||||
border: none;
|
||||
@@ -100,11 +141,64 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.add-to-cart-button:hover {
|
||||
.add-to-basket-button:hover {
|
||||
background-color: #FFFFFF;
|
||||
color: #1A1A1A;
|
||||
}
|
||||
|
||||
.add-to-cart-button:active {
|
||||
|
||||
.add-to-favorites-button:hover {
|
||||
transform: translateY(4px);
|
||||
}
|
||||
|
||||
.product-details-collapsible {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-block-end: 3em;
|
||||
}
|
||||
|
||||
.product-details-header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
border-bottom: #1A1A1A solid 1px;
|
||||
}
|
||||
|
||||
.product-details-header-arrow {
|
||||
transform: rotate(-180deg);
|
||||
margin-left: 0.5em;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
/* rotate the arrow down when the details are open */
|
||||
.product-details-header-arrow-down {
|
||||
margin-left: 0.5em;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.product-details-content {
|
||||
visibility: hidden;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.details-open {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.product-details-content-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-block-start: 0.83em;
|
||||
margin-block-end: 0.83em;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,75 @@ class ProductListing extends Component {
|
||||
|
||||
Render() {
|
||||
return {
|
||||
template: SideLoad('/components/templates/product-listing.html'),
|
||||
template: `
|
||||
<div class="product-page">
|
||||
<div class="back-button">
|
||||
<img class="back-button-svg" src="/res/back-arrow.svg" height="60em" alt="back-arrow">
|
||||
</div>
|
||||
|
||||
<div class="product-display">
|
||||
|
||||
<div class="product-image-container">
|
||||
<img class="active-image" src="{this.state.image}" alt="Image of {this.state.name}" title="Image of {this.state.name}">
|
||||
</div>
|
||||
|
||||
<div class="product-info">
|
||||
<div class="product-tags">
|
||||
${this.state.tags.map(tag => {
|
||||
return `<span class="tag">${tag}</span>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
|
||||
<div class="product-name">{this.state.name} [{this.state.date_released}]</div>
|
||||
${this.state.discount
|
||||
? '<span class="product-listing-price-full">£{this.state.price}</span><span class="product-listing-price-new">£{this.state.discount}</span>'
|
||||
: '<span class="product-listing-price">£{this.state.price}</span>'}
|
||||
<div class="product-description">{this.state.description}</div>
|
||||
|
||||
<div class="product-quantity-selector">
|
||||
<button class="product-quantity-button" type="button">-</button>
|
||||
<input class="quantity-input" type="number" value="1" min="1" max="{this.state.quantity}">
|
||||
<button class="product-quantity-button" type="button">+</button>
|
||||
</div>
|
||||
|
||||
<div class="product-add-to-basket">
|
||||
<button class="add-to-basket-button">Add to Basket</button>
|
||||
<img class="add-to-favorites-button" src="https://www.svgrepo.com/show/25921/heart.svg" width="45px" stroke="#222" stroke-width="2px" alt="Add to Favorites" title="Add to Favorites">
|
||||
</div>
|
||||
|
||||
<div class="product-details-collapsible">
|
||||
<div class="product-details-header">
|
||||
<span class="product-details-header-text">Product Details</span>
|
||||
<img class="product-details-header-arrow" src="/res/back-arrow.svg" height="30em" alt="down-arrow">
|
||||
</div>
|
||||
<div class="product-details-content">
|
||||
<div class="product-details-content-item">
|
||||
<span class="product-details-date">Released in {this.state.date_released}</span>
|
||||
</div>
|
||||
<div class="product-details-content-item">
|
||||
<span class="product-details-dimensions">Dimensions: </span>
|
||||
<span class="product-details-dimensions-value">
|
||||
{this.state.dimensions_x} x {this.state.dimensions_y} x {this.state.dimensions_z}
|
||||
</span>
|
||||
</div>
|
||||
<div class="product-details-content-item">
|
||||
<span class="product-details-weight">Weight: </span>
|
||||
<span class="product-details-weight-value">{this.state.weight}</span>
|
||||
<span class="product-details-weight-unit">g</span>
|
||||
</div>
|
||||
<div class="product-details-content-item">
|
||||
Not suitable for children under the age of 3 years old, small parts are a choking hazard.
|
||||
</div>
|
||||
<div class="product-details-content-item">
|
||||
Not for individual resale.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
style: SideLoad('/components/css/product-listing.css'),
|
||||
};
|
||||
}
|
||||
@@ -36,8 +104,20 @@ class ProductListing extends Component {
|
||||
window.history.back();
|
||||
});
|
||||
|
||||
// TODO: add event listeners for quantity buttons
|
||||
|
||||
// product details, collapsable
|
||||
const collapseButton = this.root.querySelector('.product-details-header');
|
||||
const collapseContent = this.root.querySelector('.product-details-content');
|
||||
const collapseArrow = this.root.querySelector('.product-details-header-arrow');
|
||||
|
||||
collapseButton.addEventListener('click', () => {
|
||||
collapseContent.classList.toggle('details-open');
|
||||
collapseArrow.classList.toggle('product-details-header-arrow-down');
|
||||
});
|
||||
|
||||
// add quantity to basket and then update the basket count
|
||||
const addToBasket = this.root.querySelector('.add-to-cart-button');
|
||||
const addToBasket = this.root.querySelector('.add-to-basket-button');
|
||||
const basketCount = this.root.querySelector('.quantity-input');
|
||||
|
||||
addToBasket.addEventListener('click', () => {
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<div class="product-page">
|
||||
<div class="back-button">
|
||||
<img class="back-button-svg" src="/res/back-arrow.svg" height="60em" alt="back-arrow">
|
||||
</div>
|
||||
|
||||
<div class="product-display">
|
||||
|
||||
<div class="product-image-container">
|
||||
<img class="active-image" src="{this.state.image}" alt="Image of ${this.state.name}" title="Image of {this.state.name}">
|
||||
</div>
|
||||
|
||||
<div class="product-info">
|
||||
<div class="product-name">{this.state.name}</div>
|
||||
<div class="product-price">£{this.state.price}</div>
|
||||
<script>
|
||||
if ({this.state.discount}) {
|
||||
document.querySelector('.product-price').innerHTML = `<span class="discount-price">£{this.state.price}</span> £{this.state.discount}`;
|
||||
}
|
||||
</script>
|
||||
<div class="product-description">{this.state.description}</div>
|
||||
<div class="product-quantity">
|
||||
<span class="quantity-label">Quantity:</span>
|
||||
<input class="quantity-input" type="number" value="1" min="1" step="1"/>
|
||||
</div>
|
||||
<div class="product-add-to-basket">
|
||||
<button class="add-to-cart-button">Add to Basket</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -16,10 +16,14 @@ function ValidateQuery(query) {
|
||||
async function GetSet(setId) {
|
||||
await Database.Query('BEGIN TRANSACTION;');
|
||||
const dbres = await Database.Query(`
|
||||
SELECT id, name, description, inv.price, date_released, weight, dimensions_x, dimensions_y, dimensions_z, new_price AS "discount", inv.stock, inv.last_updated AS "last_stock_update"
|
||||
SELECT lego_set.id, lego_set.name, description, tag.name AS "tag", inv.price,
|
||||
date_released, weight, dimensions_x, dimensions_y, dimensions_z,
|
||||
new_price AS "discount", inv.stock, inv.last_updated AS "last_stock_update"
|
||||
FROM lego_set
|
||||
LEFT JOIN lego_set_inventory AS inv ON inv.set_id = lego_set.id
|
||||
WHERE id = $1;
|
||||
LEFT JOIN lego_set_tag AS tags ON tags.set_id = lego_set.id
|
||||
LEFT JOIN tag AS tag ON tags.tag = tag.id
|
||||
WHERE lego_set.id = $1;
|
||||
`, [setId]);
|
||||
await Database.Query('END TRANSACTION;');
|
||||
|
||||
@@ -31,10 +35,19 @@ async function GetSet(setId) {
|
||||
};
|
||||
}
|
||||
|
||||
const tags = dbres.rows.reduce((acc, cur) => {
|
||||
acc.push(cur.tag);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const set = dbres.rows[0];
|
||||
delete set.tag;
|
||||
set.tags = tags;
|
||||
set.image = `/api/cdn/${set.id}.png`;
|
||||
set.type = 'set';
|
||||
|
||||
console.log(set)
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
@@ -44,13 +57,13 @@ async function GetSets(page, resPerPage) {
|
||||
const countRes = await Database.Query('SELECT COUNT (*) FROM lego_set;');
|
||||
const total = parseInt(countRes.rows[0].count);
|
||||
const dbres = await Database.Query(`
|
||||
SELECT
|
||||
id, name, price, new_price AS "discount"
|
||||
FROM lego_set
|
||||
LEFT JOIN lego_set_inventory as inv ON lego_set.id = inv.set_id
|
||||
ORDER BY id ASC
|
||||
LIMIT $1
|
||||
OFFSET $2;`,
|
||||
SELECT
|
||||
id, name, price, new_price AS "discount"
|
||||
FROM lego_set
|
||||
LEFT JOIN lego_set_inventory as inv ON lego_set.id = inv.set_id
|
||||
ORDER BY id ASC
|
||||
LIMIT $1
|
||||
OFFSET $2;`,
|
||||
[resPerPage, page * resPerPage]);
|
||||
await Database.Query('END TRANSACTION;');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user