fixed basket bug and added brick page
Former-commit-id: d08446b9bf6d9398ed6762b7688ce493509e4731
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
`,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
`,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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`;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user