diff --git a/README.md b/README.md
index 761e747..ab18b5a 100644
Binary files a/README.md and b/README.md differ
diff --git a/client/public/components/basket-popout.mjs b/client/public/components/basket-popout.mjs
index fabadf0..3c77269 100644
--- a/client/public/components/basket-popout.mjs
+++ b/client/public/components/basket-popout.mjs
@@ -61,8 +61,8 @@ export function RemoveProductFromBasket(product, type, amount, brickModifier = '
product += '~' + brickModifier;
}
- if (basket.items[product] > amount) {
- basket.items[product] -= amount;
+ if (basket.items[product].quantity > amount) {
+ basket.items[product].quantity -= amount;
} else {
delete basket.items[product];
}
@@ -76,6 +76,39 @@ export function RemoveProductFromBasket(product, type, amount, brickModifier = '
}
}
+export function SetProductInBasket(product, type, amount, brickModifier = 'none') {
+ if (localStorage.getItem('basket') === null || !localStorage.getItem('basket')) {
+ return;
+ }
+ const basket = JSON.parse(localStorage.getItem('basket'));
+
+ if (type === 'brick') {
+ product += '~' + brickModifier;
+ }
+
+ if (amount === 0) {
+ delete basket.items[product];
+ } else {
+ if (basket.items[product]) {
+ basket.total -= basket.items[product].quantity;
+ basket.items[product].quantity = amount;
+ } else {
+ basket.items[product] = {
+ quantity: amount,
+ type,
+ };
+ }
+ }
+
+ basket.total += amount;
+
+ localStorage.setItem('basket', JSON.stringify(basket));
+
+ if (basketCallback) {
+ basketCallback();
+ }
+}
+
export function GetBasketTotal() {
if (localStorage.getItem('basket') === null || !localStorage.getItem('basket')) {
return 0;
@@ -86,8 +119,25 @@ export function GetBasketTotal() {
return basket.total;
}
-export function GetBasketTotalPrice() {
-
+export async function GetBasketTotalPrice() {
+ if (localStorage.getItem('basket') === null || !localStorage.getItem('basket')) {
+ return 0;
+ }
+
+ const basket = JSON.parse(localStorage.getItem('basket'));
+
+ const res = await fetch('/api/basket/price', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(basket),
+ }).then(res => res.json());
+
+ if (res.error) {
+ return 0;
+ }
+ return res.data.subtotal;
}
class BasketPopout extends Component {
@@ -103,19 +153,11 @@ class BasketPopout extends Component {
if (basket) {
try {
const basketJSON = JSON.parse(basket);
-
- const res = await fetch('/api/basket/price', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(basketJSON),
- }).then(res => res.json());
-
+ const subtotal = await GetBasketTotalPrice();
this.setState({
items: basketJSON.items,
total: basketJSON.total,
- subtotal: res.data.subtotal,
+ subtotal,
});
} catch (e) {
console.log(e);
diff --git a/client/public/components/basket.mjs b/client/public/components/basket.mjs
index 04119f0..4005561 100644
--- a/client/public/components/basket.mjs
+++ b/client/public/components/basket.mjs
@@ -1,4 +1,4 @@
-import { AddProductToBasket, GetBasketTotal, RemoveProductFromBasket } from './basket-popout.mjs';
+import { AddProductToBasket, SetProductInBasket, RemoveProductFromBasket, GetBasketTotal, GetBasketTotalPrice } from './basket-popout.mjs';
import { RegisterComponent, Component } from './components.mjs';
class Basket extends Component {
@@ -32,7 +32,7 @@ class Basket extends Component {
Your Basket
@@ -60,6 +60,13 @@ class Basket extends Component {
`;
}).join('')}
+
`,
style: `
@@ -137,7 +144,16 @@ class Basket extends Component {
};
}
+ async UpdateSubtotal() {
+ const subtotal = await GetBasketTotalPrice();
+ const basketSubtotal = this.root.querySelector('.basket-subtotal');
+ if (basketSubtotal) {
+ basketSubtotal.innerText = parseFloat(subtotal).toFixed(2);
+ }
+ }
+
OnRender() {
+ this.UpdateSubtotal();
this.root.querySelectorAll('.basket-item-listing').forEach((listing) => {
// listen to mutations on the attribute stock because the stock is updated once the
// super compact listing is loaded and the stock is updated
@@ -158,7 +174,6 @@ class Basket extends Component {
...this.state,
}, false);
}
-
// update the stock number
const stockNumber = mutation.target.parentElement.querySelector('.stock-number');
stockNumber.innerText = stock;
@@ -173,6 +188,7 @@ class Basket extends Component {
});
});
+
// set up each button to update the quantity and remove if it is zero
this.root.querySelectorAll('.product-quantity-button').forEach((button) => {
button.addEventListener('click', (event) => {
@@ -197,7 +213,16 @@ class Basket extends Component {
}
if (item.quantity === 0) {
RemoveProductFromBasket(id, item.type, item.quantity, modifier);
+ this.UpdateSubtotal();
delete this.state.items[compositeId];
+
+ return this.setState({
+ ...this.state,
+ total: GetBasketTotal(),
+ items: {
+ ...this.state.items,
+ },
+ });
}
} else if (event.target.classList.contains('increase-quantity')) {
if (item.quantity < item.stock) {
@@ -206,7 +231,16 @@ class Basket extends Component {
}
} else if (event.target.classList.contains('remove-quantity')) {
RemoveProductFromBasket(id, item.type, item.quantity, modifier);
+ this.UpdateSubtotal();
delete this.state.items[compositeId];
+
+ return this.setState({
+ ...this.state,
+ total: GetBasketTotal(),
+ items: {
+ ...this.state.items,
+ },
+ });
}
// update the total
@@ -217,7 +251,72 @@ class Basket extends Component {
...this.state.items,
[compositeId]: item,
},
- });
+ }, false);
+
+ // update the total so it doesn't need to be rendered again
+ const total = this.root.querySelector('.basket-total');
+ total.innerText = this.state.total;
+ // and the same on the item
+ const itemTotal = clickedItem.querySelector('.quantity-input');
+ itemTotal.value = parseInt(item.quantity);
+ this.UpdateSubtotal();
+ });
+ });
+
+
+ // do the same but with the input field
+ this.root.querySelectorAll('.quantity-input').forEach((input) => {
+ input.addEventListener('change', (event) => {
+ let clickedItem = event.target.parentElement;
+ let listing = clickedItem.querySelector('.basket-item-listing');
+ if (!listing) {
+ clickedItem = clickedItem.parentElement;
+ listing = clickedItem.querySelector('.basket-item-listing');
+ }
+
+ const id = listing.getAttribute('id');
+ const modifier = listing.getAttribute('modifier');
+ const compositeId = id + (modifier ? `~${modifier}` : '');
+ const item = this.state.items[compositeId];
+
+ // update the quantity
+ if (event.target.value < 0) {
+ event.target.value = 0;
+ }
+ if (event.target.value > item.stock) {
+ event.target.value = item.stock;
+ }
+ item.quantity = parseInt(event.target.value);
+
+ SetProductInBasket(id, item.type, item.quantity, modifier);
+ this.UpdateSubtotal();
+
+ if (item.quantity === 0) {
+ delete this.state.items[compositeId];
+
+ return this.setState({
+ ...this.state,
+ total: GetBasketTotal(),
+ items: {
+ ...this.state.items,
+ },
+ });
+ }
+
+ // update the total
+ this.setState({
+ ...this.state,
+ total: GetBasketTotal(),
+ items: {
+ ...this.state.items,
+ [compositeId]: item,
+ },
+ }, false);
+
+
+ // update the total so it doesn't need to be rendered again
+ const total = this.root.querySelector('.basket-total');
+ total.innerText = this.state.total;
});
});
}
diff --git a/src/controllers/set-controller.js b/src/controllers/set-controller.js
index 5dc42f0..58a2e1f 100644
--- a/src/controllers/set-controller.js
+++ b/src/controllers/set-controller.js
@@ -110,7 +110,6 @@ async function SumPrices(setsArray, quantityArray) {
}
let sum = 0;
- console.log(dbres);
for (let i = 0; i < dbres.rows.length; i++) {
sum += parseFloat(dbres.rows[i].price) * parseFloat(quantityArray[i]);
}
diff --git a/src/logger.js b/src/logger.js
index 0a3e6e5..ab92b36 100644
--- a/src/logger.js
+++ b/src/logger.js
@@ -32,6 +32,18 @@ function getFormatedTimeString() {
// levelSource is the level that the source will log at ie, if levelSource is
// LEVEL_WARN, it will only log if the current level is at or above LEVEL_WARN.
function internalLog(type, message, cConsoleColour, levelSource) {
+ message = [message].map(x => {
+ if (typeof x === 'object') {
+ return JSON.stringify(x);
+ }
+
+ if (Array.isArray(x)) {
+ return x.join(' ');
+ }
+
+ return x;
+ }).join(' ');
+
if (Options.logToConsole && (Options.logLevel <= levelSource)) {
console.log(`${getFormatedTimeString()} [${cConsoleColour(type)}] ${message}`);
}
@@ -48,14 +60,18 @@ function internalLog(type, message, cConsoleColour, levelSource) {
}
}
-const Info = (...messages) => internalLog('INFO', messages.join(' '), clc.greenBright, LEVEL_INFO);
-const Warn = (...messages) => internalLog('WARN', messages.join(' '), clc.yellowBright, LEVEL_WARN);
-const Error = (...messages) => internalLog('ERROR', messages.join(' '), clc.redBright, LEVEL_STICK);
-const Panic = (...messages) => internalLog('PANIC', messages.join(' '), clc.bgRedBright, LEVEL_STICK);
-const Debug = (...messages) => internalLog('DEBUG', messages.join(' '), clc.cyanBright, LEVEL_DEBUG);
+const Info = (...messages) => internalLog('INFO', messages, clc.greenBright, LEVEL_INFO);
+const Warn = (...messages) => internalLog('WARN', messages, clc.yellowBright, LEVEL_WARN);
+const Error = (...messages) => internalLog('ERROR', messages, clc.redBright, LEVEL_STICK);
+const Panic = (...messages) => internalLog('PANIC', messages, clc.bgRedBright, LEVEL_STICK);
+const Debug = (...messages) => internalLog('DEBUG', messages, clc.cyanBright, LEVEL_DEBUG);
const Module = (module, ...messages) => internalLog(`MODULE [${module}]`, ` ${messages.join(' ')}`, clc.blue, LEVEL_INFO);
const Database = (...messages) => internalLog('PSQL', `[DB] ${messages.join(' ')}`, clc.blue, LEVEL_INFO);
const ExpressLogger = (req, res, next) => {
+ // don't spam the console with 1000000 bloody requests for thumbnails
+ if (req.originalUrl.startsWith('/api/cdn/')) {
+ return next();
+ }
internalLog('HTTP', `[${req.method}] ${req.originalUrl} FROM ${req.headers['x-forwarded-for'] || req.socket.remoteAddress}`, clc.magenta, LEVEL_VERBOSE);
next();
};
diff --git a/src/routes/helpers.js b/src/routes/helpers.js
index 56102a7..82ac7cf 100644
--- a/src/routes/helpers.js
+++ b/src/routes/helpers.js
@@ -1,5 +1,6 @@
const BrickController = require('../controllers/brick-controller.js');
const SetController = require('../controllers/set-controller.js');
+const Logger = require('../logger.js');
// AppEng Deadline
const EndDate = new Date(1651269600 * 1000);
@@ -37,12 +38,20 @@ async function CalculateBasketPrice(req, res) {
}
}
- let setSubtotal = await SetController.SumPrices(setList, setQuantities);
- let brickSubtotal = await BrickController.SumPrices(brickList, brickQuantities);
+ Logger.Debug(`Set list: ${setList} Brick list: ${brickList}`);
+ let setSubtotal = setList > 0
+ ? await SetController.SumPrices(setList, setQuantities)
+ : 0;
+ let brickSubtotal = brickList > 0
+ ? await BrickController.SumPrices(brickList, brickQuantities)
+ : 0;
+
if (setSubtotal.error) setSubtotal = 0;
if (brickSubtotal.error) brickSubtotal = 0;
+ Logger.Debug(`Set subtotal: ${setSubtotal} Brick subtotal: ${brickSubtotal} Total: ${parseFloat(setSubtotal) + parseFloat(brickSubtotal)}`);
+
res.send({
data: {
subtotal: parseFloat(setSubtotal) + parseFloat(brickSubtotal),