Skip to content

Instantly share code, notes, and snippets.

@achisholm
Created April 29, 2021 14:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save achisholm/1dcc6389ff6e356f7c887b7ac819c5aa to your computer and use it in GitHub Desktop.
Save achisholm/1dcc6389ff6e356f7c887b7ac819c5aa to your computer and use it in GitHub Desktop.
var app = {
PAGES: ["0", "1", "2", "3", "4-1", "4-2", "4-3", "5", "6", "7"],
DOM_ELEMENTS: {
buttonBack: document.getElementById('buttonBack'),
buttonContinue: document.getElementById('buttonContinue'),
buttonDeliveryEdit: document.getElementById('buttonDeliveryEdit'),
buttonGetStarted: document.getElementById("buttonGetStarted"),
buttonPlaceOrder: document.getElementById('buttonPlaceOrder'),
colourSelect: document.getElementById('colour'),
componentList: document.getElementById('componentList'),
dialogRemoveProduct: document.getElementById('dialogRemoveProduct'),
dialogRemoveProductCancel: document.getElementById('dialogRemoveProductCancel'),
dialogRemoveProductConfirm: document.getElementById('dialogRemoveProductConfirm'),
dialogDelivery: document.getElementById('dialogDelivery'),
dialogDeliveryCancel: document.getElementById('dialogDeliveryCancel'),
dialogDeliveryConfirm: document.getElementById('dialogDeliveryConfirm'),
inputFields: document.querySelectorAll('.field__input'),
orderTableTbody: document.getElementById('orderTableTbody'),
orderBasketTotal: document.getElementById('orderBasketTotal'),
orderCarriageTotal: document.getElementById('orderCarriageTotal'),
orderVatTotal: document.getElementById('orderVatTotal'),
orderTotal: document.getElementById('orderTotal'),
reviewComponentsTbody: document.getElementById('reviewComponents'),
reviewProductQtyInputField: document.getElementById('reviewProductQuantity'),
templateCable: document.getElementById('templateCable'),
templateConnector: document.getElementById('templateConnector'),
templateFlexStrip: document.getElementById('templateFlexStrip'),
templateLengthInputField: document.getElementById('templateLengthInputField'),
templateOptionContent: document.getElementById('optionContent'),
templateRadioField: document.getElementById('templateRadioField'),
templateReviewTableRow: document.getElementById('templateReviewTableRow'),
templateOrderTableRow: document.getElementById('templateOrderTableRow'),
templateQtyInputField: document.getElementById('templateQtyInputField'),
},
orderData: {},
productData: {},
componentData: {},
abortController: new AbortController(),
isLoading: false,
initialise: function () {
// Register event listeners
document.addEventListener("keypress", function (event) {
// For some elements, simulate a click when enter key is pressed during focus.
var elem = event.target;
var clickableElements = [
".js-button-back",
".js-button-continue",
".js-button-place-order",
".js-radio-field",
".js-remove-control"
];
if (elem.matches(clickableElements) && event.which == 13) {
elem.click();
}
});
app.DOM_ELEMENTS.buttonGetStarted.addEventListener('click', function (event) {
// On clicking 'Get Started', Advance to page 1.
app.changePage(1);
});
app.DOM_ELEMENTS.buttonContinue.addEventListener('click', function (event) {
// 'Continue' button click handler.
var currentPage = document.body.dataset.state;
var activePage = document.querySelector(".step-body-" + currentPage);
var invalidFields = activePage.querySelectorAll(':invalid').length;
if (invalidFields > 0) {
// Show validation message.
validationMessageShow(activePage, "Please check that all form input field values are valid and try again.");
} else {
switch (currentPage) {
case "1":
// Proceed from 'Step 1. IP Rating' to 'Step 2. Colours' page.
var ipRatingId = document.querySelector("[data-name='ip-rating'][data-checked='true']").dataset.value;
if (!app.productData.hasOwnProperty("ip_rating_id") || app.productData.ip_rating_id !== ipRatingId || colourSelect.value == "") {
// User's first visit or user has changed IP Rating selection.
this.disabled = true;
// Reset step 3.
app.productData.ip_rating_id = ipRatingId;
app.DOM_ELEMENTS.componentList.innerHTML = "";
app.setupData = {};
app.coloursPage.request(app.productData, true);
} else {
app.changePage(1);
};
break;
case "2":
// Proceed from Step 2. Colour to Step 3. Setup screen.
var colourId = colourSelect.value;
if (isEmpty(app.setupData) || productData.colour_id !== colourId) {
// User's first visit or user has changed their Colour selection.
productData.colour_id = colourId;
this.disabled = true;
// Reset step 3.
app.DOM_ELEMENTS.componentList.innerHTML = "";
app.setupData = {};
// Make AJAX request and populate step 3.
setupRequest(productData, true);
} else {
changePage(1);
}
break;
case "3":
// Proceed from Step 3. Setup to Step 4. Extras.
this.disabled = true;
recordSetupProductData();
// Make AJAX request and populate step 4.
extrasRequest(productData);
break;
case "4-1":
// Proceed from Step 4.1. to Step 4.2.
recordExtrasProductData();
changePage(1);
break;
case "4-2":
// Proceed from Step 4.2. to Step 4.3.
recordExtrasProductData();
changePage(1);
break;
case "4-3":
// Proceed from Step 4.3. Profile to Step 5. Review.
this.disabled = true;
recordExtrasProductData();
// Make AJAX request and populate step 5.
reviewRequest(productData, true);
break;
case "5":
// Proceed from 5. Review to Step 6. Order.
this.disabled = true;
recordReviewProductData();
// Populate step 6.
orderRequest(orderData, true);
break;
default:
// Proceed to the next step.
changePage(1);
}
app.saveOrderData();
}
this.blur();
});
// '.js-radio-field' click listeners
// window onpopstate // When browser history changes, setStateBasedOnURL();
// reviewProductQtyInputField 'change' listener
// reviewProductQtyInputField 'keyup' listener
// dialogRemoveProductCancel click listener
// dialogRemoveProductConfirm click listener
// buttonDeliveryEdit click listener
// dialogDeliveryCancel click listener
// dialogDeliveryConfirm click listener
// buttonPlaceOrder click listener
function initialiseApp() {
// Set up all pages based on the most recently created product.
}
},
isEmpty: function (obj) {
// Checks if an object is empty;
return Object.keys(obj).length === 0;
},
setStateBasedOnURL: function () {
// Inspect URL and switch to page given in the URL parameter.
var queryString = new URLSearchParams(window.location.search);
var page = queryString.get("page");
if (pages.includes(page)) {
document.body.dataset.state = page;
}
},
changePage: function (increment) {
// Jumps the active page backwards or forwards by a given increment.
var currentPage = document.body.dataset.state;
var index = app.PAGES.indexOf(currentPage);
var newPage = app.PAGES[index + increment];
document.body.dataset.state = newPage;
history.pushState("", "", "?page=" + newPage);
},
fetchOrderData: function () {
// Make AJAX GET request to fetch orderData JSON from the server.
},
saveOrderData: function () {
// Make AJAX POST request to save OrderData to the server.
},
isIpRatingComplete: function () {
// Checks if the productData product includes an IP Rating id.
},
isColourComplete: function () {
// Checks if the productData product has includes a Colour id.
},
isComponentsComplete: function () {
// Checks if the productData product includes valid components.
},
isProductComplete: function () { },
// Checks if the productData product is complete.
validationMessageShow: function (element) {
// Show an invalid message, appended as a child of the given element.
},
validationMessageRemove: function (element) {
// Remove any invalid messages present in the given element.
},
validateApp: function () {
// Show validation messages if pages are accessed before it's possible to populate them.
},
validationTooltipShow: function () { },
validationTooltipRemove: function () { },
checkValidity: function () { },
handleErrors: function () { },
recordReviewProductData: function () { },
orderDataProductRemove: function () {
// Removes a product from orderData
},
handleRadioFieldClick: function () { },
handleInputFieldChange: function () { },
handleReviewQtyInputChange: function () { },
handleOrderQtyInputChange: function () { },
handleRemoveButtonClick: function () {
},
handleDeliveryMethodRadioChange: function() {},
handleInputFieldChange: function () { },
handleColourSelectChange: function () { },
browserSupportsDialog: function () { },
collapseSection: function (element) { },
expandSection: function (element) { },
ipRatingPage: {
initialise: function () { },
},
coloursPage: {
initialise: function () { },
request: function () {
// Make AJAX Request to fetch Colours JSON based on the selected IP Rating.
app.DOM_ELEMENTS.buttonContinue.disabled = true;
fetch("/api/bespoke-colours.json?product_data=" + JSON.stringify(app.productData))
.then(handleErrors)
.then(function (response) {
response.json().then(function (responseData) {
// Generate the Colours options.
app.coloursPage.construct(responseData);
app.DOM_ELEMENTS.buttonContinue.disabled = false;
if (advance) {
// Proceed to next page.
changeState(1);
}
})
})
.catch(function (error) {
console.error('Error:', error);
var activePage = document.querySelector(".step-body-1");
app.validationMessageShow(activePage, "Failed to load content for next step. Please try again.");
app.DOM_ELEMENTS.buttonContinue.disabled = false;
});
},
construct: function() { }
},
setupPage: {
initialise: function () { },
request: function () { },
construct: function () { },
constructComponentCable: function () { },
constructComponentFlexStrip: function () { },
constructComponentConnector: function () { },
setAddControlsState: function() { },
recordProductData: function () { },
},
extrasPage: {
initialise: function () { },
request: function () { },
construct: function () { },
recordProductData: function () { },
getDefaultProfileLength: function () {
// Return in mm, the total length of flex strip included in the current order, rounded up to nearest multiple of 1000.
},
},
reviewPage: {
initialise: function () { },
request: function () { },
construct: function () { }
},
orderPage: {
initialise: function () { },
request: function () { },
construct: function () { }
},
orderConfirmationPage: {
request: function () { },
construct: function () { }
},
requestPlaceOrder: function () {
// AJAX request made on clicking "Place Order" button on Step 6 Order page.
},
}
app.initialise();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment