diff --git a/client/public/components/css/product-listing.css b/client/public/components/css/product-listing.css index 9d7c7e0..d6c940d 100644 --- a/client/public/components/css/product-listing.css +++ b/client/public/components/css/product-listing.css @@ -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; } diff --git a/client/public/components/product-listing.mjs b/client/public/components/product-listing.mjs index ee47c93..a098796 100644 --- a/client/public/components/product-listing.mjs +++ b/client/public/components/product-listing.mjs @@ -24,7 +24,75 @@ class ProductListing extends Component { Render() { return { - template: SideLoad('/components/templates/product-listing.html'), + template: ` +
+
+ back-arrow +
+ +
+ +
+ Image of {this.state.name} +
+ +
+
+ ${this.state.tags.map(tag => { + return `${tag}`; + }).join('')} +
+ +
{this.state.name} [{this.state.date_released}]
+ ${this.state.discount + ? '£{this.state.price}£{this.state.discount}' + : '£{this.state.price}'} +
{this.state.description}
+ +
+ + + +
+ +
+ + Add to Favorites +
+ +
+
+ Product Details + down-arrow +
+
+
+ Released in {this.state.date_released} +
+
+ Dimensions:  + + {this.state.dimensions_x} x {this.state.dimensions_y} x {this.state.dimensions_z} + +
+
+ Weight:  + {this.state.weight} + g +
+
+ Not suitable for children under the age of 3 years old, small parts are a choking hazard. +
+
+ Not for individual resale. +
+
+
+
+ +
+
+ `, 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', () => { diff --git a/client/public/components/templates/product-listing.html b/client/public/components/templates/product-listing.html deleted file mode 100644 index e58cf23..0000000 --- a/client/public/components/templates/product-listing.html +++ /dev/null @@ -1,31 +0,0 @@ -
-
- back-arrow -
- -
- -
- Image of ${this.state.name} -
- -
-
{this.state.name}
-
£{this.state.price}
- -
{this.state.description}
-
- Quantity: - -
-
- -
-
- -
-
diff --git a/src/controllers/set-controller.js b/src/controllers/set-controller.js index 204f23b..d8ac174 100644 --- a/src/controllers/set-controller.js +++ b/src/controllers/set-controller.js @@ -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;');