summaryrefslogtreecommitdiff
path: root/client/src
diff options
context:
space:
mode:
authorMike Vink <mike1994vink@gmail.com>2021-06-04 17:08:53 +0200
committerMike Vink <mike1994vink@gmail.com>2021-06-04 17:08:53 +0200
commit2f9cd43f44f8cdd4bdb16bb95a507b5f45c8e44d (patch)
tree34fd744bf9475fdc977fc697331854db61d0f15c /client/src
parent0114244663fbb8cd45a7cc4489bda469b31f0698 (diff)
server and response api
Diffstat (limited to 'client/src')
-rw-r--r--client/src/functions.js20
-rw-r--r--client/src/index.js285
-rw-r--r--client/src/shoppingbasket.js82
-rw-r--r--client/src/templateImplementations.js96
-rw-r--r--client/src/utils.js48
5 files changed, 441 insertions, 90 deletions
diff --git a/client/src/functions.js b/client/src/functions.js
new file mode 100644
index 0000000..7f06c3b
--- /dev/null
+++ b/client/src/functions.js
@@ -0,0 +1,20 @@
+// Dynamic article displaying
+export async function fetchAttractions() {
+
+ try {
+
+ const response = await fetch("api/attractions");
+
+ if (!response.ok) {
+ const message = `An error has occured: ${response.status}`;
+ throw new Error(message);
+ }
+
+ const attractions = response.json();
+ return attractions;
+
+ } catch(error) {
+ console.log("something went wrong when fetching attractions: ", error);
+ }
+
+}
diff --git a/client/src/index.js b/client/src/index.js
index 13d7219..018693f 100644
--- a/client/src/index.js
+++ b/client/src/index.js
@@ -1,75 +1,270 @@
+import { ParkArticle } from "./templateImplementations.js";
+import { displayNumberOfItemsInShoppingBasketWithBadge, dutchCurrencyFormat, dutchCurrencyFormatWithSign } from "./utils.js";
+import { fetchAttractions } from "./functions.js"
+
+
+
+function displayArticles(articles) {
+
+ const parkArticleFunctionality = {
+ orderButtonClick: orderButtonClicked,
+ displayTotal: displayTotal,
+ disableButton: disableButton,
+ }
+
+ for (var i = 0; i < articles.length; i++) {
+ var parkArticle = new ParkArticle(articles[i], document.querySelector("#parkarticle"));
+ parkArticle.addToNode(document.querySelector("#center-articles"), parkArticleFunctionality);
+ }
+
+}
+
+function disableButton(name, button) {
+ return function inputEventReceiver(event) {
+ const inputs = button.parentNode.querySelectorAll("input");
+ var inputTickets = 0;
+ for (let i = 0; i < inputs.length; i++) {
+ if (inputs[i].value > 0) {
+ inputTickets = inputTickets + Number.parseInt(inputs[i].value);
+ }
+ }
+
+ const shoppingBasketArray = JSON.parse(localStorage.getItem("shoppingBasketArray"));
+ var shoppingBasketTickets = 0;
+
+ if (shoppingBasketArray) {
+ for (let i = 0; i < shoppingBasketArray.length; i++) {
+ if (shoppingBasketArray[i].name === name) {
+ shoppingBasketTickets = inputTickets + shoppingBasketArray[i].numberOfKids + shoppingBasketArray[i].numberOfAdults;
+ }
+ }
+ }
+ const totalTickets = inputTickets + shoppingBasketTickets;
+ fetchAttractions()
+ .then(attractions => {
+
+ var attraction;
+ for (let i = 0; i < attractions.length; i++) {
+ if (attractions[i].name === name) {
+ attraction = attractions[i];
+ }
+ }
+
+ var front = button.querySelector(".front");
+ if (attraction.available < totalTickets || attraction.available === 0 || attraction.available === shoppingBasketTickets) {
+ front.classList.add("disabled");
+ button.removeEventListener("click", orderButtonClicked);
+ } else {
+ front.classList.remove("disabled");
+ button.addEventListener("click", orderButtonClicked);
+ }
+
+ })
+ .catch(error => {console.error(error)});
+ }
+}
+
function orderButtonClicked(event) {
+
console.log("button click");
var button;
+
if (event.target.classList.contains("orderbutton")) {
button = event.target;
} else {
button = event.target.parentNode;
}
- var node = button.previousElementSibling;
- var adults;
- var kids;
- var parkName;
- while (true) {
- if (node.classList.contains("numberofkids")) {
- kids = Number(node.value);
+
+ const order = button.parentNode;
+ const parkArticle = order.parentNode;
+
+ const orderClientSideInfo = {
+ name: parkArticle.querySelector(".parkname").textContent,
+ numberOfKids: Number(order.querySelector(".numberofkids").value),
+ numberOfAdults: Number(order.querySelector(".numberofadults").value),
+ }
+
+ console.log(orderClientSideInfo);
+
+ if ((orderClientSideInfo.numberOfKids > 0 && orderClientSideInfo.numberOfAdults >= 0) || (orderClientSideInfo.numberOfAdults > 0 && orderClientSideInfo.numberOfKids >= 0)) {
+ fetchAttractions()
+ .then(checkTicketAvailability(button, orderClientSideInfo))
+ .then(saveOrderInShoppingBasket(orderClientSideInfo))
+ .then(disableButton(orderClientSideInfo.name, button))
+ .catch((error) => {console.log(error.message)})
+ }
+}
+
+class TicketsNotAvailableError extends Error {
+ constructor(message) {
+ super(message);
+ this.name = "TicketsNotAvailableError";
+ }
+}
+
+
+export function checkTicketAvailability(button, orderClientSideInfo) {
+
+ return function serverAttractionsAccepter(serverAttractionsArray) {
+ var attraction;
+ for (let i = 0; i < serverAttractionsArray.length; i++) {
+ if (serverAttractionsArray[i].name === orderClientSideInfo.name) {
+ attraction = serverAttractionsArray[i]
+ }
}
- if (node.classList.contains("numberofadults")) {
- adults = Number(node.value)
+
+ if (attraction.available < orderClientSideInfo.numberOfKids + orderClientSideInfo.numberOfAdults) {
+ throw new TicketsNotAvailableError("The tickets of the order exceed the available tickets!");
}
- if (node.classList.contains("parkname")) {
- parkName = node.innerText;
- break
+
+ return serverAttractionsArray;
+ }
+}
+
+function saveOrderInShoppingBasket(orderClientSideInfo) {
+ console.log("saving in shopping basket");
+
+ return function serverAttractionsAccepter(serverAttractionsArray) {
+ // const orderClientSideInfo = this;
+
+ var price;
+ for (let i = 0; i < serverAttractionsArray.length; i++) {
+ if (serverAttractionsArray[i].name === orderClientSideInfo.name) {
+ price = calulateTotal(
+ orderClientSideInfo.numberOfKids,
+ orderClientSideInfo.numberOfAdults,
+ serverAttractionsArray[i]
+ );
+ }
}
- if (node.previousElementSibling == null) {
- node = node.parentNode;
+
+ console.log("totalprice: " + price.total);
+ console.log("discount: " + price.discount);
+ orderClientSideInfo.price = price;
+
+ var shoppingBasketArray;
+
+ if (localStorage.getItem("shoppingBasketArray") === null) {
+ shoppingBasketArray = [];
+ shoppingBasketArray.push(orderClientSideInfo);
} else {
- node = node.previousElementSibling;
+ shoppingBasketArray = JSON.parse(localStorage.getItem("shoppingBasketArray"));
+ console.log("before adding to array: " + shoppingBasketArray);
+ shoppingBasketArray.push(orderClientSideInfo);
+ console.log("after adding to array: " + shoppingBasketArray);
}
+
+ localStorage.setItem("shoppingBasketArray", JSON.stringify(shoppingBasketArray));
+ displayNumberOfItemsInShoppingBasketWithBadge();
}
+}
- if (kids > 0 || adults > 0) {
- saveOrderInShoppingBasket(parkName, adults, kids);
+function calulateTotal(numberOfKids, numberOfAdults, serverSideAttraction) {
+ console.log("Calculating total !");
+ console.log(serverSideAttraction);
+
+ const adultPrice = serverSideAttraction.adultPrice;
+ const kidsPrice = serverSideAttraction.kidsPrice;
+
+ const discountPercentage = serverSideAttraction.discount;
+ const minNumberkids = serverSideAttraction.minimumNumberOfKids;
+ const minNumberAdults = serverSideAttraction.minimumNumberOfAdults;
+
+ var totalPrice = 0;
+ if (numberOfKids > 0) {
+ totalPrice = totalPrice + numberOfKids * kidsPrice;
+ }
+ if (numberOfAdults > 0) {
+ totalPrice = totalPrice + numberOfAdults * adultPrice;
}
-};
-function saveOrderInShoppingBasket(name, adults, kids) {
- var order = {
- name: name,
- adults: adults,
- children: kids,
- };
- var orderString = JSON.stringify(order);
- localStorage.setItem(localStorage.length + 1, orderString)
- document.querySelector(".badge").innerText = localStorage.length;
+ if (numberOfKids >= minNumberkids && numberOfAdults >= minNumberAdults) {
+ var discount = totalPrice * discountPercentage / 100;
+ totalPrice = totalPrice - discount;
+ }
+
+ if (discount) {
+ return {total:totalPrice, discount: discount}
+ } else {
+ return {total: totalPrice};
+ }
}
-document.querySelector(".badge").innerText = localStorage.length;
-var buttons = document.querySelectorAll(".orderbutton");
+function displayTotal(event) {
+ var order = event.target.parentNode;
+ var total = order.querySelector(".total");
+
+ var kids = order.querySelector(".numberofkids").value;
+ var adults = order.querySelector(".numberofadults").value;
+
+ let re = /\d+/;
+ var kidsPrice = order.querySelector(".kidsprice")
+ .textContent
+ .match(re)[0];
+ var adultPrice = order.querySelector(".adultprice")
+ .textContent
+ .match(re)[0];
+
+ var discountReq = order.querySelector(".discountrequirement")
+ var minNumberkids = discountReq.querySelector(".child")
+ .textContent
+ .match(re)[0]
+ var minNumberadults = discountReq.querySelector(".adults")
+ .textContent
+ .match(re)[0]
+ var discountPercentage = discountReq.querySelector(".percentage")
+ .textContent
+ .match(re)[0];
+ // console.log(discountPercentage);
+
+ var value = 0;
+ if (kids > 0) {
+ value = value + Number.parseInt(kids) * Number.parseFloat(kidsPrice);
+ }
+ if (adults > 0) {
+ value = value + Number.parseInt(adults) * Number.parseFloat(adultPrice);
+ }
+ var discount;
+ if (Number.parseInt(kids) >= Number.parseInt(minNumberkids) && Number.parseInt(adults) >= Number.parseInt(minNumberadults)) {
+ discount = value * Number.parseFloat(discountPercentage) / 100
+ value = value - discount;
+ }
+ // console.log(value);
-for (var i = 0; i < buttons.length; i++) {
- console.log(buttons[i]);
- buttons[i].addEventListener("click", orderButtonClicked);
+ var priceString = dutchCurrencyFormat(value);
+ if (!(discount === undefined)) {
+ priceString = priceString + " discount: " + dutchCurrencyFormatWithSign(discount);
+ }
+ total.querySelector(".price").textContent = priceString;
}
-// When the user scrolls the page, execute myFunction
-window.onscroll = function() {myFunction()};
+function setStickyNavBar() {
+ // Get the header
+ var header = document.getElementById("sticky-header");
-// Get the header
-var header = document.getElementById("sticky-header");
+ // Get the offset position of the navbar
+ var sticky = header.offsetTop;
-// Get the offset position of the navbar
-var sticky = header.offsetTop;
+ // Add the sticky class to the header when you reach its scroll position. Remove "sticky" when you leave the scroll position
+ function makeHeaderStickyWhenScrolling() {
+
+ if (window.pageYOffset > sticky) {
+ header.classList.add("sticky");
+ } else {
+ header.classList.remove("sticky");
+ }
+ }
+ // Sticky navigation bar stuff
+ //
+ // When the user scrolls the page, execute myFunction
+ window.onscroll = function() {makeHeaderStickyWhenScrolling()};
-// Add the sticky class to the header when you reach its scroll position. Remove "sticky" when you leave the scroll position
-function myFunction() {
- if (window.pageYOffset > sticky) {
- header.classList.add("sticky");
- } else {
- header.classList.remove("sticky");
- }
}
+displayNumberOfItemsInShoppingBasketWithBadge();
+setStickyNavBar();
+fetchAttractions()
+ .then(displayArticles);
diff --git a/client/src/shoppingbasket.js b/client/src/shoppingbasket.js
index c23c02c..7355a99 100644
--- a/client/src/shoppingbasket.js
+++ b/client/src/shoppingbasket.js
@@ -1,70 +1,62 @@
-document.querySelector(".badge").innerText = localStorage.length;
+import { Order } from "./templateImplementations.js"
+import { displayNumberOfItemsInShoppingBasketWithBadge, findParentWithTag, childKillerUsingTags } from "./utils.js";
+
function getOrderArray() {
- var orders = new Array;
- for (let i = 0; i < localStorage.length; i++) {
- var order = localStorage.getItem(i+1);
- order = JSON.parse(order);
- orders.push(order);
- }
- // console.log(orders);
+ var orders = JSON.parse(localStorage.getItem("shoppingBasketArray"));
return orders;
}
-class Order {
- constructor(orderJSON) {
- for (const [key, value] of Object.entries(orderJSON)) {
- this[key] = value;
- }
-
- }
-
- addToMain() {
- // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template
- var main = document.querySelector("main");
- var template = document.querySelector("#ticket");
+function displayOrders() {
+ displayNumberOfItemsInShoppingBasketWithBadge();
+ const orderFunctionality = {
+ cancel: cancelOrder,
+ };
- var clone = template.content.cloneNode(true);
- // console.log(clone);
- var lines = clone.querySelectorAll("div");
+ var orders = getOrderArray();
+ if (orders === null) return;
- for (var i = 0; i < lines.length; i++) {
- var text = lines[i].textContent;
+ var main = document.querySelector("main");
- if (text === "Parkname") {
- console.log(this.name);
- lines[i].textContent = this.name;
- }
+ for (let i = 0; i < orders.length; i++) {
+ var orderObj = new Order(orders[i], document.querySelector("#ticket"));
+ orderObj.addToNode(main, orderFunctionality);
+ }
+}
- if (text.toLowerCase().includes("adults")) {
- lines[i].textContent = text + " " + this.adults;
- }
+function cancelOrder(event) {
+ //console.log(event.target);
+ const article = findParentWithTag.bind(event.target)("article");
- if (text.toLowerCase().includes("kids")) {
- lines[i].textContent = text + " " + this.children;
- }
+ var previous = article.previousSibling;
+ var i = 0;
+ while (previous) {
+ if (previous.tagName === "ARTICLE") {
+ i = i+1;
}
-
- main.appendChild(clone);
+ previous = previous.previousSibling;
}
-}
-
-function displayOrders() {
var orders = getOrderArray();
- for (let i = 0; i < orders.length; i++) {
- orderObj = new Order(orders[i]);
- orderObj.addToMain();
- }
+ orders.splice(i, 1);
+ localStorage.setItem("shoppingBasketArray", JSON.stringify(orders));
+ var main = document.querySelector("main");
+ childKillerUsingTags(main)(main.firstChild)("article");
+ displayOrders();
}
-displayOrders();
+
+
function finalizePayment(event) {
console.log("finalizing payments");
localStorage.clear();
+
window.location.replace("orderplaced.html");
}
+
document.querySelector("#finalizepaymentbutton").addEventListener("click", finalizePayment);
+
+displayOrders();
diff --git a/client/src/templateImplementations.js b/client/src/templateImplementations.js
new file mode 100644
index 0000000..24b8c9a
--- /dev/null
+++ b/client/src/templateImplementations.js
@@ -0,0 +1,96 @@
+import { dutchCurrencyFormat, dutchCurrencyFormatWithSign } from "./utils.js"
+import { fetchAttractions } from "./functions.js"
+/**
+ * Abstract class
+ *
+ *
+ * @class TemplatedNode
+ */
+class TemplatedNode {
+ constructor(json, template) {
+ // console.log(json);
+ for (const [key, value] of Object.entries(json)) {
+ this[key] = value;
+ }
+
+ this.template = template;
+ }
+
+ addToNode(node) {
+ throw new Error("Method 'addToNode' node be implemented.");
+ }
+}
+
+
+export class Order extends TemplatedNode {
+
+ addToNode(node, orderFunctionality) {
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template
+
+ var clone = this.template.content.cloneNode(true);
+ // console.log(clone);
+ clone.querySelector(".parkname").textContent = this.name;
+ clone.querySelector(".numberofkids").textContent = "Kids: " + this.numberOfKids;
+ clone.querySelector(".numberofadults").textContent = "Adults: " + this.numberOfAdults;
+
+
+ var priceString = "Total: " + dutchCurrencyFormat(this.price.total);
+ if (this.price.discount) {
+ priceString = priceString + " discount: " + dutchCurrencyFormatWithSign(this.price.discount);
+ }
+ clone.querySelector(".price").textContent = priceString;
+
+ clone.querySelector("button").addEventListener("click", orderFunctionality.cancel);
+
+ node.appendChild(clone);
+ }
+
+}
+
+export class ParkArticle extends TemplatedNode {
+
+ addToNode(node, parkArticleFunctionality) {
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template
+
+ var clone = this.template.content.cloneNode(true);
+ this.clone = clone;
+ var cloneChildren = clone.querySelectorAll("div");
+
+ for (var i = 0; i < cloneChildren.length; i++) {
+ // console.log(cloneChildren[i].className);
+ var element = cloneChildren[i];
+ var className = element.className;
+ var text = element.textContent;
+ if (className === "parkname") {
+ element.textContent = this.name;
+ }
+
+ if (className === "parkdescription") {
+ element.textContent = this.description;
+ }
+
+ if (className === "adultprice") {
+ element.querySelector(".price").textContent = dutchCurrencyFormat(this.adultPrice);
+ }
+
+ if (className === "kidsprice") {
+ element.querySelector(".price").textContent = dutchCurrencyFormat(this.kidsPrice);
+ }
+
+ if (className === "discountrequirement") {
+ element.querySelector(".adults").textContent = this.minimumNumberOfAdults;
+ element.querySelector(".child").textContent = this.minimumNumberOfKids;
+ }
+ }
+
+ var button = clone.querySelector(".orderbutton");
+ parkArticleFunctionality.disableButton(this.name, button)(null);
+
+ var inputElements = clone.querySelectorAll("input");
+ for (let i = 0; i < inputElements.length; i++) {
+ inputElements[i].addEventListener("input", parkArticleFunctionality.displayTotal);
+ inputElements[i].addEventListener("input", parkArticleFunctionality.disableButton(this.name, button));
+ }
+ node.appendChild(clone);
+ }
+}
diff --git a/client/src/utils.js b/client/src/utils.js
new file mode 100644
index 0000000..03416f0
--- /dev/null
+++ b/client/src/utils.js
@@ -0,0 +1,48 @@
+export function displayNumberOfItemsInShoppingBasketWithBadge() {
+ var shoppingBasketArray = JSON.parse(localStorage.getItem("shoppingBasketArray"));
+ if (shoppingBasketArray === null) {
+ document.querySelector(".badge").innerText = 0;
+ } else {
+ document.querySelector(".badge").innerText = shoppingBasketArray.length;
+ }
+}
+
+export function dutchCurrencyFormat(number) {
+ var decimal = (number * 10) % 10
+ if (decimal === 0) return number + ",-";
+ else {
+ return (number * 10 - decimal) / 10 + "," + decimal;
+ }
+}
+
+export function dutchCurrencyFormatWithSign(number) {
+ return "\u20AC" + dutchCurrencyFormat(number);
+}
+
+
+export function findParentWithTag(tagName) {
+ if (this.tagName === tagName.toUpperCase()) {
+ return this;
+ } else {
+ return findParentWithTag.bind(this.parentNode)(tagName);
+ }
+}
+
+export function childKillerUsingTags(parent) {
+
+ return function oneOfMyChildren(child) {
+
+ return function killChildrenWithTag(tag) {
+ if (child === null) {
+ return
+ } else if (child.tagName === tag.toUpperCase()) {
+ var next = child.nextSibling;
+ parent.removeChild(child);
+ return oneOfMyChildren(next)(tag);
+ } else {
+ return oneOfMyChildren(child.nextSibling)(tag);
+ }
+ }
+
+ }
+}