ampersand search query bug fix
Former-commit-id: 5b72eeabea7458d1bcbc13f9642b39350b1b3267
This commit is contained in:
@@ -14,35 +14,189 @@ class Basket extends Component {
|
||||
this.setState({
|
||||
...basketItems,
|
||||
}, false);
|
||||
console.log(basketItems, this.state);
|
||||
} else {
|
||||
this.setState({
|
||||
items: {},
|
||||
total: 0,
|
||||
}, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Render() {
|
||||
return {
|
||||
template: /* html */`
|
||||
<span class="tag">{this.state.name}</span>
|
||||
<div class="basket">
|
||||
<div class="basket-header">
|
||||
<div class="basket-header-title">
|
||||
Your Basket
|
||||
</div>
|
||||
<div class="basket-header-total">
|
||||
Total: {this.state.total} items
|
||||
</div>
|
||||
</div>
|
||||
<div class="basket-items">
|
||||
<div class="basket-items-list">
|
||||
${Object.keys(this.state.items).map((key) => {
|
||||
const item = this.state.items[key];
|
||||
console.log(key, item);
|
||||
const modifier = key.includes('~');
|
||||
return /* html */`
|
||||
<div class="basket-item">
|
||||
<super-compact-listing-component class="basket-item-listing"
|
||||
id="${key.split('~')[0]}"
|
||||
type="${item.type}"
|
||||
bigimage="true"
|
||||
${modifier ? `modifier="${key.split('~')[1]}"` : ''}>
|
||||
</super-compact-listing-component>
|
||||
<div class="product-quantity-selector">
|
||||
<button class="product-quantity-button reduce-quantity" type="button">-</button>
|
||||
<input class="quantity-input" type="number" value="${item.quantity}" min="0" max="{item.stock}">
|
||||
<button class="product-quantity-button increase-quantity" type="button">+</button>
|
||||
<span class="product-quantity"> <span class="stock-number">0</span> in stock</span>
|
||||
</div>
|
||||
<button class="product-quantity-button remove-quantity" type="button">Remove</button>
|
||||
</div>
|
||||
`;
|
||||
}).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
style: `
|
||||
.tag {
|
||||
font-size: 0.8em;
|
||||
padding: 0.2em 0.5em;
|
||||
margin-right: 0.3em;
|
||||
margin-top: 0.2em;
|
||||
margin-bottom: 0.2em;
|
||||
line-height: 1.3em;
|
||||
font-weight: bold;
|
||||
background-color: #F2CA52;
|
||||
.basket {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.basket-header {
|
||||
display: flex;
|
||||
font-size: 2em;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.basket-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.basket-item-listing {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.product-quantity-selector {
|
||||
flex-basis: 40%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.product-quantity-button {
|
||||
cursor: pointer;
|
||||
font-size: 1.7em;
|
||||
border: #1A1A1A solid 1px;
|
||||
background-color: #F5F6F6;
|
||||
border-radius: 0.2em;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
.remove-quantity {
|
||||
font-size: 1em;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
/* https://www.w3schools.com/howto/howto_css_hide_arrow_number.asp */
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
.quantity-input {
|
||||
height: 2.2em;
|
||||
width: 3.7em;
|
||||
background-color: #F5F6F6;
|
||||
border-top: #1A1A1A solid 1px;
|
||||
border-bottom: #1A1A1A solid 1px;
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
OnRender() {
|
||||
this.root.addEventListener('click', () => {
|
||||
this.root.classList.toggle('tag-selected');
|
||||
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
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.attributeName === 'stock') {
|
||||
const stock = parseInt(mutation.target.getAttribute('stock'));
|
||||
|
||||
// update the stock number
|
||||
const stockNumber = mutation.target.parentElement.querySelector('.stock-number');
|
||||
stockNumber.innerText = stock;
|
||||
const stockMax = mutation.target.parentElement.querySelector('.quantity-input');
|
||||
stockMax.setAttribute('max', stock);
|
||||
}
|
||||
});
|
||||
});
|
||||
observer.observe(listing, {
|
||||
attributes: true,
|
||||
attributeFilter: ['stock'],
|
||||
});
|
||||
});
|
||||
|
||||
// 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) => {
|
||||
const item = event.target.parentElement.parentElement;
|
||||
const id = item.getAttribute('id');
|
||||
const type = item.getAttribute('type');
|
||||
const modifier = item.getAttribute('modifier');
|
||||
const quantity = parseInt(item.querySelector('.quantity-input').value);
|
||||
const stock = parseInt(item.querySelector('.stock-number').innerText);
|
||||
|
||||
// update the quantity
|
||||
if (event.target.classList.contains('reduce-quantity')) {
|
||||
if (quantity > 0) {
|
||||
item.querySelector('.quantity-input').value = quantity - 1;
|
||||
}
|
||||
} else if (event.target.classList.contains('increase-quantity')) {
|
||||
if (quantity < stock) {
|
||||
item.querySelector('.quantity-input').value = quantity + 1;
|
||||
}
|
||||
} else if (event.target.classList.contains('remove-quantity')) {
|
||||
// remove the item from the basket
|
||||
// delete this.state.items[id];
|
||||
// this.setState({
|
||||
// items: this.state.items,
|
||||
// }, true);
|
||||
}
|
||||
|
||||
// update the total
|
||||
this.setState({
|
||||
total: Object.keys(this.state.items).reduce((total, key) => {
|
||||
const item = this.state.items[key];
|
||||
return total + (item.quantity * item.price);
|
||||
}, 0),
|
||||
}, true);
|
||||
|
||||
// update the basket in local storage
|
||||
// localStorage.setItem('basket', JSON.stringify(this.state));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +111,7 @@ class ProductListing extends Component {
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
console.log(this.state);
|
||||
|
||||
return {
|
||||
template: /* html */`
|
||||
<div class="product-page">
|
||||
|
||||
@@ -118,6 +118,8 @@ class Search extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
value = encodeURIComponent(value);
|
||||
|
||||
const route = `/api/search?q=${value}&per_page=10`;
|
||||
fetch(route).then((response) => {
|
||||
return response.json();
|
||||
@@ -148,8 +150,10 @@ class Search extends Component {
|
||||
|
||||
if (e.keyCode === 13) {
|
||||
const searchTerm = e.target.value;
|
||||
if (searchTerm.length > 0) {
|
||||
window.location.href = `/search?q=${searchTerm}`;
|
||||
const encodedSearchTerm = encodeURIComponent(searchTerm);
|
||||
|
||||
if (encodedSearchTerm.length > 0) {
|
||||
window.location.href = `/search?q=${encodedSearchTerm}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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'; }
|
||||
|
||||
@@ -18,6 +17,8 @@ class SuperCompactProductListing extends Component {
|
||||
const tags = product.tags;
|
||||
const colours = product.colours;
|
||||
|
||||
this.setAttribute('stock', product.stock);
|
||||
|
||||
this.setState({
|
||||
...this.getState,
|
||||
name,
|
||||
@@ -53,7 +54,7 @@ class SuperCompactProductListing extends Component {
|
||||
<img class="product-image"
|
||||
title="Image of {this.state.name}"
|
||||
alt="Image of {this.state.name}"
|
||||
src="/api/cdn/${this.state.id}-thumb.png">
|
||||
src="/api/cdn/${this.state.id}${this.state.bigimage ? '' : '-thumb'}.png">
|
||||
</span>
|
||||
${modifierPreview}
|
||||
<span class="product-listing-info">
|
||||
@@ -61,7 +62,7 @@ class SuperCompactProductListing extends 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('')
|
||||
? this.state.tags.map(tag => `<tag-component name="${tag}"></tag-component>`).join('')
|
||||
: `<tag-component name="${this.state.tag}"></tag-component>`}
|
||||
</span>
|
||||
</span>
|
||||
@@ -108,6 +109,8 @@ class SuperCompactProductListing extends Component {
|
||||
}
|
||||
|
||||
.product-image {
|
||||
max-height: 150px;
|
||||
max-width: 150px;
|
||||
object-fit: scale-down;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class Tag extends Component {
|
||||
|
||||
OnRender() {
|
||||
this.root.addEventListener('click', () => {
|
||||
this.root.classList.toggle('tag-selected');
|
||||
window.location.href = `/search?q=${this.state.name}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
</ul>
|
||||
</li>
|
||||
<li class="account-item menu-item"><a class="account-button nav-link" href="#">My Account</a>
|
||||
<li class="menu-item stock-mode" style="display: none;">
|
||||
<li class="menu-item stock-mode" style="display: flex;">
|
||||
STOCK MODE
|
||||
<label class="switch">
|
||||
<input type="checkbox">
|
||||
|
||||
@@ -43,8 +43,8 @@
|
||||
console.log(`/api/search/${window.location.search}`)
|
||||
// parse out the search query using the query object
|
||||
const query = new URLSearchParams(window.location.search)
|
||||
// get the search query
|
||||
const searchQuery = query.get('q')
|
||||
// get the search query from the query object
|
||||
const searchQuery = query.get('q');
|
||||
document.write(/* html */`
|
||||
<product-list-component id="results"
|
||||
title="Search Results for '${searchQuery}'"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -19,6 +19,13 @@ async function CalculateBasketPrice(req, res) {
|
||||
const brickList = [];
|
||||
const brickQuantities = [];
|
||||
|
||||
if (!req.body.items) {
|
||||
res.send({
|
||||
error: 'No items in basket',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [item, value] of Object.entries(req.body.items)) {
|
||||
if (value.type === 'set') {
|
||||
setList.push(item.split('~')[0]);
|
||||
|
||||
Reference in New Issue
Block a user