Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Quick Add Bulk] Don't block quantity input during cart API call #3461

Merged
merged 16 commits into from
May 13, 2024
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"editor.formatOnSave": false,
"[javascript]": {
"editor.formatOnSave": true
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
sofiamatulis marked this conversation as resolved.
Show resolved Hide resolved
},
"[css]": {
"editor.formatOnSave": true
Expand Down
75 changes: 57 additions & 18 deletions assets/bulk-add.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,80 @@
class BulkAdd extends HTMLElement {
sofiamatulis marked this conversation as resolved.
Show resolved Hide resolved
constructor() {
super();
this.queue = []
this.queue = [];
this.requestStarted = false;
this.ids = []
this.ids = [];
}

startQueue(id, quantity) {
this.queue.push({id, quantity})
this.queue.push({ id, quantity });
const interval = setInterval(() => {
if (this.queue.length > 0) {
if (!this.requestStarted) {
this.sendRequest(this.queue)
if (!this.requestStarted) {
this.sendRequest(this.queue);
}
} else {
clearInterval(interval)
clearInterval(interval);
}
}, 250)
}, 250);
}


sendRequest(queue) {
this.requestStarted = true;
const items = {}
const ids = []
const items = {};
const ids = [];
sofiamatulis marked this conversation as resolved.
Show resolved Hide resolved
queue.forEach((queueItem) => {
items[parseInt(queueItem.id)] = queueItem.quantity;
ids.push(queueItem.id)
ids.push(queueItem.id);
});
this.queue = this.queue.filter(queueElement => !queue.includes(queueElement));
const quickOrderList = this.closest('quick-order-list');
quickOrderList.updateMultipleQty(items, ids)
this.queue = this.queue.filter((queueElement) => !queue.includes(queueElement));
const element = this.closest('quick-order-list') || this.closest('quick-add-bulk');
sofiamatulis marked this conversation as resolved.
Show resolved Hide resolved
element.updateMultipleQty(items, ids);
}
}

if (!customElements.get('bulk-add')) {
customElements.define('bulk-add', BulkAdd)
};
resetQuantityInput(id) {
const input = document.getElementById(`Quantity-${id}`);
sofiamatulis marked this conversation as resolved.
Show resolved Hide resolved
input.value = input.getAttribute('value');
sofiamatulis marked this conversation as resolved.
Show resolved Hide resolved
this.isEnterPressed = false;
}

setValidity(event, index, message) {
event.target.setCustomValidity(message);
event.target.reportValidity();
this.resetQuantityInput(index);
event.target.select();
}

validateQuantity(event) {
const inputValue = parseInt(event.target.value);
const index = event.target.dataset.index;

if (inputValue < event.target.dataset.min) {
this.setValidity(event, index, window.quickOrderListStrings.min_error.replace('[min]', event.target.dataset.min));
} else if (inputValue > parseInt(event.target.max)) {
this.setValidity(event, index, window.quickOrderListStrings.max_error.replace('[max]', event.target.max));
} else if (inputValue % parseInt(event.target.step) != 0) {
this.setValidity(event, index, window.quickOrderListStrings.step_error.replace('[step]', event.target.step));
} else {
event.target.setCustomValidity('');
event.target.reportValidity();
this.startQueue(index, inputValue);
}
}

getSectionsUrl() {
if (window.pageNumber) {
return `${window.location.pathname}?page=${window.pageNumber}`;
} else {
return `${window.location.pathname}`;
}
}

getSectionInnerHTML(html, selector) {
return new DOMParser().parseFromString(html, 'text/html').querySelector(selector).innerHTML;
}
}

if (!customElements.get('bulk-add')) {
customElements.define('bulk-add', BulkAdd);
}
sofiamatulis marked this conversation as resolved.
Show resolved Hide resolved
167 changes: 54 additions & 113 deletions assets/quick-add-bulk.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
if (!customElements.get('quick-add-bulk')) {
customElements.define(
'quick-add-bulk',
class QuickAddBulk extends HTMLElement {
class QuickAddBulk extends BulkAdd {
constructor() {
super();
this.quantity = this.querySelector('quantity-input');

const debouncedOnChange = debounce((event) => {
if (parseInt(event.target.dataset.cartQuantity) === 0) {
this.addToCart(event);
if (parseInt(event.target.value) === 0) {
this.startQueue(event.target.dataset.index, parseInt(event.target.value));
} else {
this.updateCart(event);
this.validateQuantity(event);
}
}, ON_CHANGE_DEBOUNCE_TIMER);

Expand Down Expand Up @@ -65,12 +65,6 @@ if (!customElements.get('quick-add-bulk')) {
});
}

resetQuantityInput(id) {
const input = document.getElementById(id);
input.value = input.getAttribute('value');
this.isEnterPressed = false;
}

cleanErrorMessageOnType(event) {
event.target.addEventListener(
'keypress',
Expand Down Expand Up @@ -102,81 +96,35 @@ if (!customElements.get('quick-add-bulk')) {
});
}

updateCart(event) {
this.lastActiveInputId = event.target.getAttribute('data-index');
this.quantity.classList.add('quantity__input-disabled');
updateMultipleQty(items, ids) {
this.selectProgressBar().classList.remove('hidden');

const body = JSON.stringify({
quantity: event.target.value,
id: event.target.getAttribute('data-index'),
updates: items,
sections: this.getSectionsToRender().map((section) => section.section),
sections_url: this.getSectionsUrl(),
});

fetch(`${routes.cart_change_url}`, { ...fetchConfig('javascript'), ...{ body } })
fetch(`${routes.cart_update_url}`, { ...fetchConfig(), ...{ body } })
.then((response) => {
return response.text();
})
.then((state) => {
const parsedState = JSON.parse(state);
this.quantity.classList.remove('quantity__input-disabled');
if (parsedState.description || parsedState.errors) {
event.target.setCustomValidity(parsedState.description);
event.target.reportValidity();
this.resetQuantityInput(event.target.id);
this.selectProgressBar().classList.add('hidden');
event.target.select();
this.cleanErrorMessageOnType(event);
return;
}

this.renderSections(parsedState);

this.renderSections(parsedState, ids);
publish(PUB_SUB_EVENTS.cartUpdate, { source: 'quick-add', cartData: parsedState });
})
.catch((error) => {
console.log(error, 'error');
});
}

addToCart(event) {
this.quantity.classList.add('quantity__input-disabled');
this.selectProgressBar().classList.remove('hidden');
this.lastActiveInputId = event.target.getAttribute('data-index');
const body = JSON.stringify({
items: [
{
quantity: parseInt(event.target.value),
id: parseInt(this.dataset.id),
},
],
sections: this.getSectionsToRender().map((section) => section.section),
});

fetch(`${routes.cart_add_url}`, { ...fetchConfig('javascript'), ...{ body } })
.then((response) => {
return response.text();
})
.then((state) => {
const parsedState = JSON.parse(state);
this.quantity.classList.remove('quantity__input-disabled');
if (parsedState.description || parsedState.errors) {
event.target.setCustomValidity(parsedState.description);
event.target.reportValidity();
this.resetQuantityInput(event.target.id);
this.selectProgressBar().classList.add('hidden');
event.target.select();
this.cleanErrorMessageOnType(event);
// Error handling
return;
}

this.renderSections(parsedState);

publish(PUB_SUB_EVENTS.cartUpdate, { source: 'quick-add', cartData: parsedState });
.catch(() => {
// e.target.setCustomValidity(error);
sofiamatulis marked this conversation as resolved.
Show resolved Hide resolved
// e.target.reportValidity();
// this.resetQuantityInput(ids[index]);
// this.selectProgressBar().classList.add('hidden');
// e.target.select();
// this.cleanErrorMessageOnType(e);
})
.catch((error) => {
console.error(error);
.finally(() => {
this.selectProgressBar().classList.add('hidden');
this.requestStarted = false;
});
}

Expand All @@ -200,52 +148,45 @@ if (!customElements.get('quick-add-bulk')) {
];
}

getSectionsUrl() {
if (window.pageNumber) {
return `${window.location.pathname}?page=${window.pageNumber}`;
} else {
return `${window.location.pathname}`;
}
}

getSectionInnerHTML(html, selector) {
return new DOMParser().parseFromString(html, 'text/html').querySelector(selector).innerHTML;
}
renderSections(parsedState, ids) {
const intersection = this.queue.filter((element) => ids.includes(element.id));
if (intersection.length === 0) {
sofiamatulis marked this conversation as resolved.
Show resolved Hide resolved
this.getSectionsToRender().forEach((section) => {
const sectionElement = document.getElementById(section.id);
if (
sectionElement &&
sectionElement.parentElement &&
sectionElement.parentElement.classList.contains('drawer')
) {
parsedState.items.length > 0
? sectionElement.parentElement.classList.remove('is-empty')
: sectionElement.parentElement.classList.add('is-empty');

setTimeout(() => {
document
.querySelector('#CartDrawer-Overlay')
.addEventListener('click', this.cart.close.bind(this.cart));
});
}
const elementToReplace =
sectionElement && sectionElement.querySelector(section.selector)
? sectionElement.querySelector(section.selector)
: sectionElement;
if (elementToReplace) {
elementToReplace.innerHTML = this.getSectionInnerHTML(
parsedState.sections[section.section],
section.selector
);
}
});

renderSections(parsedState) {
this.getSectionsToRender().forEach((section) => {
const sectionElement = document.getElementById(section.id);
if (
sectionElement &&
sectionElement.parentElement &&
sectionElement.parentElement.classList.contains('drawer')
) {
parsedState.items.length > 0
? sectionElement.parentElement.classList.remove('is-empty')
: sectionElement.parentElement.classList.add('is-empty');

setTimeout(() => {
document.querySelector('#CartDrawer-Overlay').addEventListener('click', this.cart.close.bind(this.cart));
});
}
const elementToReplace =
sectionElement && sectionElement.querySelector(section.selector)
? sectionElement.querySelector(section.selector)
: sectionElement;
if (elementToReplace) {
elementToReplace.innerHTML = this.getSectionInnerHTML(
parsedState.sections[section.section],
section.selector
);
if (this.isEnterPressed) {
this.querySelector(`#Quantity-${this.lastActiveInputId}`).select();
}
});

if (this.isEnterPressed) {
this.querySelector(`#Quantity-${this.lastActiveInputId}`).select();
this.listenForActiveInput();
this.listenForKeydown();
}

this.listenForActiveInput();
this.listenForKeydown();
}
}
);
Expand Down
Loading
Loading