More joining

Former-commit-id: 690f0bf7b00ecfb89043bbcea77616e8d9913bc4
This commit is contained in:
Ben
2022-04-23 00:17:19 +01:00
parent 3301e111e2
commit 8dd5c8d63d
14 changed files with 150 additions and 116 deletions

View File

@@ -1,43 +0,0 @@
<html>
<head>
<title>LegoLog: About</title>
<meta name="viewport">
<link rel="icon" type="image/svg+xml" href="/res/favicon.svg">
<link rel="stylesheet" type="text/css" href="../global.css">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Londrina+Solid&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Josefin+Sans&display=swap" rel="stylesheet">
<!-- Auth0 - a library for authentication -->
<script src="https://cdn.auth0.com/js/auth0-spa-js/1.13/auth0-spa-js.production.js"></script>
<!-- Flickity - a library to make carousells easier to implement -->
<link rel="stylesheet" href="https://unpkg.com/flickity@2/dist/flickity.min.css">
<script src="https://unpkg.com/flickity@2/dist/flickity.pkgd.min.js"></script>
<!-- Components used on this page - they must be included to work -->
<script type="module" src="/components/components.mjs"></script>
<script type="module" src="/components/navbar.mjs"></script>
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>
<script type="module" src="/components/storefront.mjs"></script>
<script type="module" src="/components/tag.mjs"></script>
<script type="module" src="/components/product-list.mjs"></script>
<script type="module" src="/components/super-compact-listing.mjs"></script>
<script type="module" src="/components/compact-listing.mjs"></script>
<script type="module" src="/components/product-listing.mjs"></script>
<script type="module" src="index.mjs"></script>
</head>
<body>
<notificationbar-component></notificationbar-component>
<navbar-component></navbar-component>
<limited-margin>
</limited-margin>
</body>

View File

@@ -139,7 +139,8 @@ class BasketPopout extends Component {
<super-compact-listing-component class="sc-listing"
id="${key.split('~')[0]}"
type="${item.type}"
quantity="${item.quantity}">
quantity="${item.quantity}"
modifier="${key.split('~')[1] || ''}">
</super-compact-listing-component>
</div>
`;
@@ -224,6 +225,7 @@ class BasketPopout extends Component {
}
.popup-content-item {
background-color: #F5F6F6;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
@@ -237,6 +239,7 @@ class BasketPopout extends Component {
}
.sc-listing {
flex-basis: 100%;
flex-grow: 3;
}

View File

@@ -7,6 +7,17 @@ class CompactProductListing extends Component {
super(CompactProductListing);
}
OnMount() {
console.log(this.state);
if (this.state.tags) {
const tags = JSON.parse(this.state.tags);
this.setState({
...this.getState,
tags,
}, false);
}
}
Render() {
return {
template: /* html */`
@@ -20,6 +31,9 @@ class CompactProductListing extends Component {
<div class="product-listing-info">
<div class="product-listing-name">{this.state.name} {this.state.id}</div>
</a>
${this.state.tags
? this.state.tags.map(tag => `<tag-component name="${tag}"></tag-component>`).join('')
: ''}
${this.state.discount
? `<span class="product-listing-price-full">£${parseFloat(this.state.price).toFixed(2)}</span><span class="product-listing-price-new">£${parseFloat(this.state.discount).toFixed(2)}</span>`
: `<span class="product-listing-price">£${parseFloat(this.state.price).toFixed(2)}</span>`}

View File

@@ -38,9 +38,12 @@ class ProductList extends Component {
this.keepLoading = true;
this.loadingBar = `
<!--Infinite Loading-->
<div class="product-list-loader">
<!-- https://loading.io/css/ -->
<div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
<div class="product-list-loader-container-container">
<div class="product-list-loader-container">
<div class="product-list-loader">
<img src="/res/loading.gif" height="100" alt="Loading...">
</div>
</div>
</div>
`;
}
@@ -55,6 +58,7 @@ class ProductList extends Component {
listing="${product.listing}"
price="${product.price}"
type="${product.type}"
tags="${JSON.stringify(product.tags).replace(/"/g, '&quot;')}"
discount="${product.discount || ''}"></compact-listing-component>
`;
}).join('')}
@@ -86,66 +90,49 @@ class ProductList extends Component {
border: none;
}
.product-list-loader {
.product-list-loader-container-container {
display: flex;
justify-content: center;
}
.lds-ellipsis {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
z-index: 0;
@keyframes grow-shrink {
0% {
transform: scale(1);
}
50% {
transform: scale(1.4);
}
100% {
transform: scale(1);
}
}
.lds-ellipsis div {
position: absolute;
top: 33px;
width: 13px;
height: 13px;
@keyframes shrink-grow {
0% {
transform: scale(1.4);
}
50% {
transform: scale(0.9);
}
100% {
transform: scale(1.4);
}
}
.product-list-loader-container {
display: flex;
justify-content: center;
width: 100px;
border-radius: 50%;
background: #7F5CFF;
animation-timing-function: cubic-bezier(0, 1, 1, 0);
background-color: #D7C2FF;
/* grow and shrink sine wave */
animation-timing-function: ease-in-out;
animation: grow-shrink 1s infinite;
}
.lds-ellipsis div:nth-child(1) {
left: 8px;
animation: lds-ellipsis1 0.6s infinite;
}
.lds-ellipsis div:nth-child(2) {
left: 8px;
animation: lds-ellipsis2 0.6s infinite;
}
.lds-ellipsis div:nth-child(3) {
left: 32px;
animation: lds-ellipsis2 0.6s infinite;
}
.lds-ellipsis div:nth-child(4) {
left: 56px;
animation: lds-ellipsis3 0.6s infinite;
}
@keyframes lds-ellipsis1 {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes lds-ellipsis3 {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
@keyframes lds-ellipsis2 {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(24px, 0);
}
.product-list-loader {
animation-timing-function: ease-in-out;
animation: shrink-grow 1s infinite;
}
@media (pointer:none), (pointer:coarse), screen and (max-width: 900px) {

View File

@@ -103,7 +103,7 @@ class Search extends Component {
const res = /* html */`
<super-compact-listing-component class="sc-listing" id="${result.id}"
name="${result.name}"
tag="${result.tag}"
tags="${JSON.stringify(result.tags).replace(/"/g, '&quot;')}"
type="${result.type}"
price="${result.discount || result.price}">
</super-compact-listing-component>

View File

@@ -19,10 +19,10 @@ class StoreFront extends Component {
</div>
</div>
<div class="carousel-cell">
<img class="carousel-image" src="res/warehouse.png" alt="">
<img class="carousel-image" src="res/technic.png" alt="">
<div class="carousel-caption">
<h1>Our state of the art warehouse ensures your speedy delivery</h1>
<a href="/about/"><button>Find Out More</button></a>
<h1>Check out our LEGO® Technic range</h1>
<a href="/search/?q=technic"><button>Show Technic Now</button></a>
</div>
</div>
<div class="carousel-cell">

View File

@@ -13,18 +13,39 @@ class SuperCompactProductListing extends Component {
if (!this.state.name || !this.state.price) {
const product = (await fetch(`/api/${this.state.type}/${this.state.id}`).then(res => res.json())).data;
const name = product.name;
const price = (product.discount || product.price) * this.state.quantity || 1;
const price = product.discount || product.price;
const tag = product.tag;
const tags = product.tags;
const colours = product.colours;
this.setState({
...this.getState,
name,
price,
tag,
tags,
colours,
quantity: product.quantity,
}, false);
} else if (this.state.tags) {
const tags = JSON.parse(this.state.tags);
this.setState({
...this.getState,
tags,
}, false);
}
}
Render() {
let modifierPreview = '';
if (this.state.modifier) {
if (this.state.modifier !== '0') {
modifierPreview = /* html */`
<span class="brick-colour-demonstrator" style="background-color: #${this.state.colours[this.state.modifier].hexrgb}"></span>
`;
}
}
return {
template: /* html */`
<span class="product-listing">
@@ -33,11 +54,16 @@ class SuperCompactProductListing extends Component {
title="Image of {this.state.name}"
alt="Image of {this.state.name}"
src="/api/cdn/${this.state.id}-thumb.png">
</span>
</span>
${modifierPreview}
<span class="product-listing-info">
<span class="product-listing-name">{this.state.name}</span>
<div class="product-listing-modifier">${this.state.modifier || ''}</div>
<tag-component name="{this.state.tag}"></tag-component>
<div class="product-listing-modifier">${this.state.modifier ? `Colour: ${this.state.colours[this.state.modifier].name}` : ''}</div>
<span class="product-listing-tags">
${this.state.tags
? this.state.tags.map(tag => `<tag-component name="${tag}"></tag-component>`).join('')
: `<tag-component name="${this.state.tag}"></tag-component>`}
</span>
</span>
<span class="product-pricing">
£${parseFloat(this.state.price).toFixed(2)}
@@ -68,6 +94,18 @@ class SuperCompactProductListing extends Component {
max-width: 100%;
flex-grow: 1
}
.brick-colour-demonstrator {
position: absolute;
margin: 0 auto;
margin-bottom: 7px;
align-self: flex-start;
left: 0;
width: 30px;
height: 30px;
margin-right: 0.5em;
border: #1A1A1A solid 1px;
}
.product-image {
object-fit: scale-down;
@@ -101,7 +139,7 @@ class SuperCompactProductListing extends Component {
font-size: 0.8em;
font-weight: bold;
color: #E55744;
}
}
`,
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 KiB

View File

@@ -0,0 +1,2 @@

View File

@@ -94,6 +94,7 @@ async function GetBulkBricks(bricksArr) {
async function GetBrick(brickId) {
await Database.Query('BEGIN TRANSACTION;');
const dbres = await Database.Query(`
SELECT lego_brick.id, lego_brick.name, tag.name AS "tag",
inv.price, inv.new_price AS "discount", inv.stock,

View File

@@ -55,7 +55,7 @@ function LevenshteinDistance(s, t) {
function SanatiseQuery(query) {
query = query.trim();
query = query.replace(/[^a-zA-Z0-9\s]/g, '');
query = query.replace(/[^a-zA-Z0-9,/\s]/g, '');
query = escape(query);
query = query.toLowerCase();
return query;

View File

@@ -22,7 +22,7 @@ async function Search(fuzzyString) {
}
// order by levenshtine distance
const sets = dbres.rows;
let sets = dbres.rows;
sets.sort((a, b) => {
const aName = a.name.toLowerCase();
const bName = b.name.toLowerCase();
@@ -54,9 +54,19 @@ async function Search(fuzzyString) {
// combine tags into a single array
for (const set of sets) {
set.type = 'set';
set.tags = set.tag.split(',');
set.tags = [];
}
// combine (joined) rows into a single array
sets = sets.reduce((arr, current) => {
if (!arr.some(item => item.id === current.id)) {
arr.push(current);
}
arr.find(item => item.id === current.id).tags.push(current.tag);
return arr;
}, []);
return sets;
}
@@ -109,9 +119,11 @@ async function GetSets(page, resPerPage) {
const total = parseInt(countRes.rows[0].count);
const dbres = await Database.Query(`
SELECT
id, name, price, new_price AS "discount"
lego_set.id, lego_set.name, price, new_price AS "discount", tag.name AS "tag"
FROM lego_set
LEFT JOIN lego_set_inventory as inv ON lego_set.id = inv.set_id
LEFT JOIN lego_set_tag AS tags ON tags.set_id = lego_set.id
LEFT JOIN tag AS tag ON tags.tag = tag.id
ORDER BY id ASC
LIMIT $1
OFFSET $2;`,
@@ -126,12 +138,23 @@ async function GetSets(page, resPerPage) {
};
}
const sets = dbres.rows;
let sets = dbres.rows;
for (const set of sets) {
set.type = 'set';
set.tags = [];
}
// combine (joined) rows into a single array
sets = sets.reduce((arr, current) => {
if (!arr.some(item => item.id === current.id)) {
arr.push(current);
}
arr.find(item => item.id === current.id).tags.push(current.tag);
return arr;
}, []);
return { total, sets };
}

View File

@@ -50,8 +50,14 @@ async function Query(query, params, callback) {
// debug moment
Logger.Database(`PSQL Query: ${query.substring(0, 500).trim()}...`);
const result = await connection.query(query, params, callback);
return result;
try {
const result = await connection.query(query, params, callback);
return result;
} catch (err) {
Logger.Database(`PSQL Query Error: ${err.message}`);
connection.query('ROLLBACK TRANSACTION;');
throw err;
}
}
async function Destroy() {

View File

@@ -16,6 +16,9 @@ function Get(req, res) {
id = id.replace('-thumb', '');
}
// TODO: bricks with a modifier should show the modified colour in the thumbnail
// I HAVE NO IDEA HOW TO DO THIS WITHOUT A LOT OF WORK
// this very randomly fails sometimes
try {
// work out hash from id