product display
Former-commit-id: 860d840458933eae6910fee505d849655d88ffbc
This commit is contained in:
@@ -41,7 +41,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.product-display {
|
.product-display {
|
||||||
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-info {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +60,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-name {
|
.product-name {
|
||||||
@@ -66,15 +71,42 @@
|
|||||||
margin-block-end: 0.83em;
|
margin-block-end: 0.83em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-price {
|
.product-tags {
|
||||||
font-size: 1.5em;
|
width: 100%;
|
||||||
font-weight: bold;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
margin-block-start: 0.83em;
|
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 {
|
.product-description {
|
||||||
font-size: 1.5em;
|
font-size: 1em;
|
||||||
margin-block-start: 0.83em;
|
margin-block-start: 0.83em;
|
||||||
margin-block-end: 0.83em;
|
margin-block-end: 0.83em;
|
||||||
}
|
}
|
||||||
@@ -88,9 +120,18 @@
|
|||||||
text-align: center;
|
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-start: 0.83em;
|
||||||
margin-block-end: 0.83em;
|
margin-block-end: 0.83em;
|
||||||
|
margin-right: 1em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: #1A1A1A;
|
background-color: #1A1A1A;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -100,11 +141,64 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-to-cart-button:hover {
|
.add-to-basket-button:hover {
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
color: #1A1A1A;
|
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() {
|
Render() {
|
||||||
return {
|
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'),
|
style: SideLoad('/components/css/product-listing.css'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -36,8 +104,20 @@ class ProductListing extends Component {
|
|||||||
window.history.back();
|
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
|
// 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');
|
const basketCount = this.root.querySelector('.quantity-input');
|
||||||
|
|
||||||
addToBasket.addEventListener('click', () => {
|
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) {
|
async function GetSet(setId) {
|
||||||
await Database.Query('BEGIN TRANSACTION;');
|
await Database.Query('BEGIN TRANSACTION;');
|
||||||
const dbres = await Database.Query(`
|
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
|
FROM lego_set
|
||||||
LEFT JOIN lego_set_inventory AS inv ON inv.set_id = lego_set.id
|
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]);
|
`, [setId]);
|
||||||
await Database.Query('END TRANSACTION;');
|
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];
|
const set = dbres.rows[0];
|
||||||
|
delete set.tag;
|
||||||
|
set.tags = tags;
|
||||||
set.image = `/api/cdn/${set.id}.png`;
|
set.image = `/api/cdn/${set.id}.png`;
|
||||||
set.type = 'set';
|
set.type = 'set';
|
||||||
|
|
||||||
|
console.log(set)
|
||||||
|
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,13 +57,13 @@ async function GetSets(page, resPerPage) {
|
|||||||
const countRes = await Database.Query('SELECT COUNT (*) FROM lego_set;');
|
const countRes = await Database.Query('SELECT COUNT (*) FROM lego_set;');
|
||||||
const total = parseInt(countRes.rows[0].count);
|
const total = parseInt(countRes.rows[0].count);
|
||||||
const dbres = await Database.Query(`
|
const dbres = await Database.Query(`
|
||||||
SELECT
|
SELECT
|
||||||
id, name, price, new_price AS "discount"
|
id, name, price, new_price AS "discount"
|
||||||
FROM lego_set
|
FROM lego_set
|
||||||
LEFT JOIN lego_set_inventory as inv ON lego_set.id = inv.set_id
|
LEFT JOIN lego_set_inventory as inv ON lego_set.id = inv.set_id
|
||||||
ORDER BY id ASC
|
ORDER BY id ASC
|
||||||
LIMIT $1
|
LIMIT $1
|
||||||
OFFSET $2;`,
|
OFFSET $2;`,
|
||||||
[resPerPage, page * resPerPage]);
|
[resPerPage, page * resPerPage]);
|
||||||
await Database.Query('END TRANSACTION;');
|
await Database.Query('END TRANSACTION;');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user