diff --git a/client/public/about/index.html b/client/public/about/index.html
deleted file mode 100644
index eb72cf4..0000000
--- a/client/public/about/index.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
{this.state.name} {this.state.id}
+ ${this.state.tags
+ ? this.state.tags.map(tag => `
`).join('')
+ : ''}
${this.state.discount
? `
£${parseFloat(this.state.price).toFixed(2)}£${parseFloat(this.state.discount).toFixed(2)}`
: `
£${parseFloat(this.state.price).toFixed(2)}`}
diff --git a/client/public/components/product-list.mjs b/client/public/components/product-list.mjs
index b2d717c..23e75df 100644
--- a/client/public/components/product-list.mjs
+++ b/client/public/components/product-list.mjs
@@ -38,9 +38,12 @@ class ProductList extends Component {
this.keepLoading = true;
this.loadingBar = `
-
-
-
+
+
+
+

+
+
`;
}
@@ -55,6 +58,7 @@ class ProductList extends Component {
listing="${product.listing}"
price="${product.price}"
type="${product.type}"
+ tags="${JSON.stringify(product.tags).replace(/"/g, '"')}"
discount="${product.discount || ''}">
`;
}).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) {
diff --git a/client/public/components/search.mjs b/client/public/components/search.mjs
index 7ea3839..c885dc5 100644
--- a/client/public/components/search.mjs
+++ b/client/public/components/search.mjs
@@ -103,7 +103,7 @@ class Search extends Component {
const res = /* html */`
diff --git a/client/public/components/storefront.mjs b/client/public/components/storefront.mjs
index 0f16b2f..32c7e2b 100644
--- a/client/public/components/storefront.mjs
+++ b/client/public/components/storefront.mjs
@@ -19,10 +19,10 @@ class StoreFront extends Component {
diff --git a/client/public/components/super-compact-listing.mjs b/client/public/components/super-compact-listing.mjs
index 447231b..e915e68 100644
--- a/client/public/components/super-compact-listing.mjs
+++ b/client/public/components/super-compact-listing.mjs
@@ -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 */`
+
+ `;
+ }
+ }
+
return {
template: /* html */`
@@ -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">
-
+
+ ${modifierPreview}
{this.state.name}
- ${this.state.modifier || ''}
-
+ ${this.state.modifier ? `Colour: ${this.state.colours[this.state.modifier].name}` : ''}
+
+ ${this.state.tags
+ ? this.state.tags.map(tag => ``).join('')
+ : ``}
+
£${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;
- }
+ }
`,
};
}
diff --git a/client/public/res/technic.png b/client/public/res/technic.png
new file mode 100644
index 0000000..0511b5f
Binary files /dev/null and b/client/public/res/technic.png differ
diff --git a/src/controllers/bigram.js b/src/controllers/bigram.js
new file mode 100644
index 0000000..99a8091
--- /dev/null
+++ b/src/controllers/bigram.js
@@ -0,0 +1,2 @@
+
+
diff --git a/src/controllers/brick-controller.js b/src/controllers/brick-controller.js
index 1c2ca3f..6a2415a 100644
--- a/src/controllers/brick-controller.js
+++ b/src/controllers/brick-controller.js
@@ -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,
diff --git a/src/controllers/controller-master.js b/src/controllers/controller-master.js
index f1ba238..01ac29d 100644
--- a/src/controllers/controller-master.js
+++ b/src/controllers/controller-master.js
@@ -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;
diff --git a/src/controllers/set-controller.js b/src/controllers/set-controller.js
index 52189e7..ae78771 100644
--- a/src/controllers/set-controller.js
+++ b/src/controllers/set-controller.js
@@ -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 };
}
diff --git a/src/database/database.js b/src/database/database.js
index c3c6c1c..2006c1c 100644
--- a/src/database/database.js
+++ b/src/database/database.js
@@ -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() {
diff --git a/src/routes/cdn.js b/src/routes/cdn.js
index 14560d8..86a3a00 100644
--- a/src/routes/cdn.js
+++ b/src/routes/cdn.js
@@ -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