fixed basket bug and added brick page

Former-commit-id: d08446b9bf6d9398ed6762b7688ce493509e4731
This commit is contained in:
Ben
2022-04-15 17:21:23 +01:00
parent ebf27b1ac0
commit 3ed429f0f4
13 changed files with 114 additions and 76 deletions

BIN
README.md

Binary file not shown.

View File

@@ -20,7 +20,7 @@ let basketCallback = null;
// TODO: Does the localstorage have a problem with mutual exclusion?
// TODO: Should the basket be persisted to the server?
export function AddProductToBasket(product, amount) {
export function AddProductToBasket(product, type, amount) {
if (localStorage.getItem('basket') === null || !localStorage.getItem('basket')) {
localStorage.setItem('basket', JSON.stringify({
items: {},
@@ -30,10 +30,13 @@ export function AddProductToBasket(product, amount) {
const basket = JSON.parse(localStorage.getItem('basket'));
if (basket.items.product) {
basket.items.product += amount;
if (basket.items[product]) {
basket.items[product].quantity += amount;
} else {
basket.items.product = amount;
basket.items[product] = {
quantity: amount,
type,
};
}
basket.total += amount;
@@ -45,16 +48,16 @@ export function AddProductToBasket(product, amount) {
}
}
export function RemoveProductFromBasket(item, amount) {
export function RemoveProductFromBasket(product, amount) {
if (localStorage.getItem('basket') === null || !localStorage.getItem('basket')) {
return;
}
const basket = JSON.parse(localStorage.getItem('basket'));
if (basket.items.item > amount) {
basket.items.item -= amount;
if (basket.items[product] > amount) {
basket.items[product] -= amount;
} else {
delete basket.items.item;
delete basket.items[product];
}
basket.total -= amount;

View File

@@ -21,8 +21,8 @@ class CompactProductListing extends Component {
<div class="product-listing-name">{this.state.name} {this.state.id}</div>
</a>
${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>'}
? `<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>`}
</div>
</div>
`,

View File

@@ -233,7 +233,7 @@ input[type=number] {
height: 400px;
}
.set-piece-container {
.set-brick-container {
width: 100%;
display: flex;
flex-direction: row;
@@ -249,7 +249,7 @@ input[type=number] {
min-width: 0;
}
.set-piece-amount {
.set-brick-amount {
flex-grow: 1;
font-size: 2.3em;
font-weight: bold;

View File

@@ -16,25 +16,32 @@ class ProductListing extends Component {
const getProductURL = new URL(`/api/${type}/${id}`, document.baseURI);
const productData = await fetch(getProductURL).then(response => response.json());
let setContents = [];
if (productData.data.type === 'set') {
const allPieces = [];
Object.keys(productData.data.includedPieces).forEach(key => {
allPieces.push(key);
});
this.state.image = `/api/cdn/${productData.data.id}.png`;
const bulkSets = await fetch('/api/bulk/brick', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
ids: allPieces,
}),
}).then(response => response.json());
setContents = bulkSets.data;
if (productData.data.type !== 'set') {
this.setState({
...this.getState,
...productData.data,
}, false);
return;
}
let setContents = [];
const allBricks = [];
Object.keys(productData.data.includedBricks).forEach(key => {
allBricks.push(key);
});
const bulkSets = await fetch('/api/bulk/brick', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
ids: allBricks,
}),
}).then(response => response.json());
setContents = bulkSets.data;
this.setState({
...this.getState,
...productData.data,
@@ -44,7 +51,6 @@ class ProductListing extends Component {
Render() {
let setContents = '';
console.log(this.state)
if (this.state.type === 'set') {
setContents = /* html */`
<div class="collapsible-menu">
@@ -53,14 +59,14 @@ class ProductListing extends Component {
<img class="menu-header-arrow" src="/res/back-arrow.svg" height="30em" alt="down-arrow">
</div>
<div class="menu-content scrollable-container">
${this.state.setContents.map(piece => /* html */`
<div class="set-piece-container">
<span class="set-piece-amount">x${this.state.includedPieces[piece.id]}</span>
<super-compact-listing-component class="sc-listing" id="${piece.id}"
name="${piece.name}"
tag="${piece.tag}"
type="piece"
price="${piece.price || piece.discount}">
${this.state.setContents.map(brick => /* html */`
<div class="set-brick-container">
<span class="set-brick-amount">x${this.state.includedBricks[brick.id]}</span>
<super-compact-listing-component class="sc-listing" id="${brick.id}"
name="${brick.name}"
tag="${brick.tag}"
type="brick"
price="${brick.price || brick.discount}">
</super-compact-listing-component>
</div>
`).join('')}
@@ -91,9 +97,9 @@ class ProductListing extends Component {
<div class="product-name">{this.state.name} {this.state.id}</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>
? `<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>`}
<div class="product-description">${this.state.description || this.state.name + ' ' + this.state.tag}</div>
<div class="product-quantity-selector">
<button class="product-quantity-button reduce-quantity" type="button">-</button>
@@ -113,7 +119,7 @@ class ProductListing extends Component {
<img class="menu-header-arrow" src="/res/back-arrow.svg" height="30em" alt="down-arrow">
</div>
<div class="menu-content">
<div class="product-details-content-item">Released in {this.state.date_released}</div>
${this.state.date_released ? /* html */'<div class="product-details-content-item">Released in {this.state.date_released}</div>' : ''}
<div class="product-details-content-item">Dimensions: {this.state.dimensions_x} x {this.state.dimensions_y} x {this.state.dimensions_z}</div>
<div class="product-details-content-item">Weight: {this.state.weight}g</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>
@@ -183,7 +189,7 @@ class ProductListing extends Component {
const addToBasket = this.root.querySelector('.add-to-basket-button');
addToBasket.addEventListener('click', () => {
AddProductToBasket(this.state.id, Math.abs(parseInt(quantityInput.value)));
AddProductToBasket(this.state.id, this.state.type, Math.abs(parseInt(quantityInput.value)));
quantityInput.value = 1;
});
}

View File

@@ -1,5 +1,7 @@
import { RegisterComponent, Component } from './components.mjs';
// super compact listing is interoperable through types which makes it exteremeely poggers and also portable
class SuperCompactProductListing extends Component {
static __IDENTIFY() { return 'super-compact-listing'; }
@@ -22,7 +24,7 @@ class SuperCompactProductListing extends Component {
<tag-component name="{this.state.tag}"></tag-component>
</span>
<span class="product-pricing">
£{this.state.price}
£${parseFloat(this.state.price).toFixed(2)}
</span>
</span>
`,

View File

@@ -142,22 +142,22 @@ for (let i = 0; i < setIds.length; i++) {
console.log(`NEW set tag ${i} ${setId}`);
}
// then pieces in sets
addLine('\n-- pieces in sets\n');
const newPieceInSet = (setId, brickId, quantity) => `INSERT INTO set_descriptor (set_id, brick_id, amount) VALUES ('${setId}', '${brickId}', '${quantity}');`;
// then bricks in sets
addLine('\n-- bricks in sets\n');
const newBrickInSet = (setId, brickId, quantity) => `INSERT INTO set_descriptor (set_id, brick_id, amount) VALUES ('${setId}', '${brickId}', '${quantity}');`;
const setDescriptors = JSON.parse(fs.readFileSync('db/res/sets.json'));
for (const setId of setIds) {
const set = setDescriptors[setId];
for (const [brickId, quantity] of Object.entries(set)) {
console.log(`NEW piece in set ${setId} ${brickId}`);
addLine(newPieceInSet(setId, brickId, quantity));
console.log(`NEW brick in set ${setId} ${brickId}`);
addLine(newBrickInSet(setId, brickId, quantity));
}
}
// then make up some random data for brick inventory
addLine('\n-- piece inventory\n');
addLine('\n-- brick inventory\n');
const newBrickInventory = (brickId, stock, price, newPrice) => `INSERT INTO lego_brick_inventory (brick_id, stock, price, new_price, last_updated) VALUES ('${brickId}', '${stock}', '${price}', '${newPrice || ''}', now());`;
const newBrickInventoryWNULL = (brickId, stock, price) => `INSERT INTO lego_brick_inventory (brick_id, stock, price, last_updated) VALUES ('${brickId}', '${stock}', '${price}', now());`;
@@ -236,5 +236,5 @@ addLine('\n-- bonsai tree\n');
addLine(newSet('1010', 'Lego Bonsai Tree', 100, 'Lego Bonsai Tree by Matthew Dennis, Rich Boakes and Jacek Kopecky', 2022, 100, 100, 100));
addLine(newSetTag('1010', '1201'));
addLine(newSetTag('1010', '323'));
addLine(newPieceInSet('1010', '95228', 1000));
addLine(newBrickInSet('1010', '95228', 1000));
addLine(newSetInventory('1010', 5, 1000.69, 50.00));

View File

@@ -5635,7 +5635,7 @@ INSERT INTO lego_set_tag (set_id, tag) VALUES ('41325-1', '771');
INSERT INTO lego_set_tag (set_id, tag) VALUES ('75969-1', '227');
INSERT INTO lego_set_tag (set_id, tag) VALUES ('42123-1', '36');
-- pieces in sets
-- bricks in sets
INSERT INTO set_descriptor (set_id, brick_id, amount) VALUES ('76023-1', '476', '2');
INSERT INTO set_descriptor (set_id, brick_id, amount) VALUES ('76023-1', '2357', '6');
@@ -14320,7 +14320,7 @@ INSERT INTO set_descriptor (set_id, brick_id, amount) VALUES ('42123-1', '32523p
INSERT INTO set_descriptor (set_id, brick_id, amount) VALUES ('42123-1', '11946pb057', '1');
INSERT INTO set_descriptor (set_id, brick_id, amount) VALUES ('42123-1', '11947pb057', '1');
-- piece inventory
-- brick inventory
INSERT INTO lego_brick_inventory (brick_id, stock, price, last_updated) VALUES ('476', '32985', '0.71', now());
INSERT INTO lego_brick_inventory (brick_id, stock, price, new_price, last_updated) VALUES ('2357', '24825', '5.01', '0.6', now());

View File

@@ -49,7 +49,7 @@ async function main() {
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
// for sets use https://img.bricklink.com/ItemImage/SL/${part[2]}.png
// for for pieces use https://img.bricklink.com/ItemImage/PL/${part[2]}.png
// for for bricks use https://img.bricklink.com/ItemImage/PL/${part[2]}.png
// https://img.bricklink.com/ItemImage/PL/3962a.png
const url = `https://img.bricklink.com/ItemImage/PL/${part[2]}.png`;
const filename = `res/image/${part[2]}.png`;

View File

@@ -1,4 +1,4 @@
// scrapes bricklink for the every piece and amounts in a set of lego
// scrapes bricklink for the every brick and amounts in a set of lego
const fs = require('fs');
const axios = require('axios');
@@ -6,7 +6,7 @@ const axios = require('axios');
const sets = fs.readFileSync('res/Sets.txt', 'utf8').toString().split('\n').map((i) => i.split('\t'));
// output format:
// setid: { pieceid: amount, pieceid: amount, ... }
// setid: { brickid: amount, brickid: amount, ... }
async function post(url) {
// axios return HTML from website
@@ -32,18 +32,18 @@ async function main() {
output[set[2]] = {};
let pieceCount = 0;
let brickCount = 0;
let m;
while ((m = regex.exec(data)) !== null) {
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
pieceCount += parseInt(m[2]);
brickCount += parseInt(m[2]);
output[set[2]] = { ...output[set[2]], [m[1]]: parseInt(m[2]) };
}
console.log(`${i}/${sets.length} ${set[2]} has ${pieceCount} pieces`);
console.log(`${i}/${sets.length} ${set[2]} has ${brickCount} bricks`);
fs.writeFileSync('res/sets.json', JSON.stringify(output));
}
}

View File

@@ -31,7 +31,40 @@ 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,
inv.last_updated AS "last_stock_update",
weight, dimensions_x, dimensions_y, dimensions_z
FROM lego_brick
LEFT JOIN lego_brick_inventory AS inv on inv.brick_id = lego_brick.id
LEFT JOIN lego_brick_tag AS tags ON tags.brick_id = lego_brick.id
LEFT JOIN tag AS tag ON tags.tag = tag.id
WHERE lego_brick.id = $1;
`, [brickId]);
const colDbres = await Database.Query(`
SELECT lego_brick_colour.id, lego_brick_colour.name, lego_brick_colour.hexrgb,
colour_type.name AS "colour_type"
FROM lego_brick_colour
LEFT JOIN colour_type AS colour_type ON lego_brick_colour.col_type = colour_type.id
`, []);
await Database.Query('END TRANSACTION;');
// validate database response
if (dbres.rows.length === 0) {
return {
error: 'Brick not found',
long: 'The brick you are looking for does not exist',
};
}
const brick = dbres.rows[0];
brick.colours = colDbres.rows;
brick.tags = brick.tag.split(',');
brick.type = 'brick';
return brick;
}
module.exports = {

View File

@@ -1,18 +1,6 @@
const ControllerMaster = require('./controller-master.js');
const Database = require('../database/database.js');
function ValidateQuery(query) {
const validation = ControllerMaster.ValidateQuery(query);
if (!validation.isValid) {
return {
error: validation.error,
long: validation.longError,
};
}
return true;
}
async function GetSet(setId) {
await Database.Query('BEGIN TRANSACTION;');
const dbres = await Database.Query(`
@@ -42,16 +30,15 @@ async function GetSet(setId) {
return acc;
}, new Set());
const pieces = dbres.rows.reduce((acc, cur) => {
const bricks = dbres.rows.reduce((acc, cur) => {
acc[cur.brick_id] = cur.amount;
return acc;
}, {});
const set = dbres.rows[0];
delete set.tag;
set.includedPieces = pieces;
set.includedBricks = bricks;
set.tags = Array.from(tags);
set.image = `/api/cdn/${set.id}.png`;
set.type = 'set';
return set;
@@ -91,7 +78,6 @@ async function GetSets(page, resPerPage) {
}
module.exports = {
ValidateQuery,
GetSet,
GetSets,
};

View File

@@ -1,8 +1,16 @@
const Controller = require('../controllers/brick-controller.js');
function Get(req, res) {
async function Get(req, res) {
const id = req.params.id;
const brick = await Controller.GetBrick(id);
if (brick.error) {
res.send(JSON.stringify(brick));
return;
}
res.send(JSON.stringify({
message: 'Hello World!',
data: brick,
}));
}