summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorMike Vink <mike1994vink@gmail.com>2021-06-10 18:41:21 +0200
committerMike Vink <mike1994vink@gmail.com>2021-06-10 18:41:21 +0200
commit22ee6924ca6a59ecf4ca0030daab302f697b3445 (patch)
tree7990b4cf02754ca42e27585d28dd67fddaaf0bf3 /client
parentfd46c28539cf7c130dfbb0458f7b9057ad4f1e46 (diff)
big progress
Diffstat (limited to 'client')
-rw-r--r--client/admin.html42
-rw-r--r--client/index.html17
-rw-r--r--client/orders.html16
-rw-r--r--client/shoppingbasket.html8
-rw-r--r--client/src/admin.js28
-rw-r--r--client/src/adminArticle.js117
-rw-r--r--client/src/attractionArticle.js253
-rw-r--r--client/src/functions.js21
-rw-r--r--client/src/index.js264
-rw-r--r--client/src/map.js22
-rw-r--r--client/src/myorders.js24
-rw-r--r--client/src/orderArticle.js34
-rw-r--r--client/src/shoppingbasket.js49
-rw-r--r--client/src/templateImplementations.js73
-rw-r--r--client/src/utils.js79
-rw-r--r--client/style/admin.css29
-rw-r--r--client/style/main.css29
-rw-r--r--client/style/map.css119
18 files changed, 846 insertions, 378 deletions
diff --git a/client/admin.html b/client/admin.html
index 291f79f..eb8f9a5 100644
--- a/client/admin.html
+++ b/client/admin.html
@@ -8,6 +8,7 @@
<meta name="description" content="The ticket shop for Sogyo employees">
<link rel="stylesheet" href="style/main.css">
+ <link rel="stylesheet" href="style/admin.css">
</head>
<body>
@@ -41,9 +42,44 @@
</nav>
<main>
+ <div class="newattraction" style="">
+ <button class="orderbutton" id="addbutton">
+ <span class="front">
+ Add
+ </span>
+ </button>
+ <div class="newattractioninput"><label>New name:</label><input id="newattractionname" placeholder="new attraction name"/></div>
+ <div class="newattractioninput"><label>Description:</label><input id="newattractiondescription" placeholder="description"/></div>
+ <div class="newattractioninput"><label>Lat:</label><input .class="newattractioninput" id="newlat" placeholder="lat gps position"/></div>
+ <div class="newattractioninput"><label>Long:</label><input .class="newattractioninput" id="newlong" placeholder="long gps position"/></div>
+ </div>
+ </main>
+
+ <template id="adminattraction">
+ <article>
+ <div class="parkname"></div>
+ <div class="info">
+ <div class="prices">
+ <div class="adultprice">Adult price: <span class="sign">&euro;</span><input type="number" class="adultPrice"/></div>
+ <div class="kidsprice">Kids price: <span class="sign">&euro;</span><input type="number" class="kidsPrice"/></div>
+ <div class="discountrequirement">
+ <em>Discount requirement:</em>
+ Buy <input type="number" class="minimumNumberOfAdults"/> adult tickets & <input type="number" class="minimumNumberOfKids"/> kid tickets for a <input type="number" class="discount"/>% discount!
+ </div>
+ </div>
+ <div class="availabletickets">Available tickets: <input type="number" class="available"/></div>
+ </div>
+ <button class="orderbutton" id="deletebutton">
+ <span class="front">
+ Delete
+ </span>
+ </button>
+ </article>
+ </template>
+
+
+ <script type="module" src="src/admin.js"></script>
-
- </main>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/client/index.html b/client/index.html
index af3f5dd..62021bf 100644
--- a/client/index.html
+++ b/client/index.html
@@ -42,6 +42,23 @@
</header>
<main>
+ <div id="sortmenu">
+ <div class="sorter" id="pricesorter">
+ <label>Sort on price:</label>
+ <button class="orderbutton" id="sortprice">
+ <span class="front">
+ </span>
+ </button>
+ </div>
+ <div class="sorter" id="locationsorter">
+ <label>Sort on location:</label>
+ <button class="orderbutton" id="sortprice">
+ <span class="front">
+ </span>
+ </button>
+ </div>
+ </div>
+
<div id="center-articles">
</div>
diff --git a/client/orders.html b/client/orders.html
index ab936be..31c6686 100644
--- a/client/orders.html
+++ b/client/orders.html
@@ -45,6 +45,18 @@
</main>
- <script src="src/myorders.js"></script>
+ <template id="ticket">
+ <article>
+ <div class="parkname"></div>
+ <div class="orderdescription">
+ <div class="numberofadults"></div>
+ <div class="numberofkids"></div>
+ <div class="price"></div>
+ </div>
+ </article>
+ </template>
+
+
+ <script type="module" src="src/myorders.js"></script>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/client/shoppingbasket.html b/client/shoppingbasket.html
index ae7ebb3..06143b3 100644
--- a/client/shoppingbasket.html
+++ b/client/shoppingbasket.html
@@ -51,9 +51,11 @@
<template id="ticket">
<article>
<div class="parkname"></div>
- <div class="numberofadults"></div>
- <div class="numberofkids"></div>
- <div class="price"></div>
+ <div class="orderdescription">
+ <div class="numberofadults"></div>
+ <div class="numberofkids"></div>
+ <div class="price"></div>
+ </div>
<button class="orderbutton cancel">
<span class="front cancel">
Cancel order
diff --git a/client/src/admin.js b/client/src/admin.js
index e69de29..a857055 100644
--- a/client/src/admin.js
+++ b/client/src/admin.js
@@ -0,0 +1,28 @@
+import { AdminAttraction } from "./templateImplementations.js"
+
+import {
+ fetchAttractions,
+} from "./utils.js"
+
+import {
+ addAttractionInDatabase
+} from "./adminArticle.js"
+
+function displayAdminAttractions(attractions) {
+ console.log(attractions);
+ const main = document.querySelector("main");
+ const template = document.querySelector("#adminattraction");
+
+ for (let i = 0; i < attractions.length; i++) {
+ const attraction = attractions[i];
+ const adminAttraction = new AdminAttraction(attraction, template);
+ adminAttraction.addToNode(main);
+ }
+}
+
+const newattraction = document.querySelector(".newattraction");
+addbutton.addEventListener("click", addAttractionInDatabase(newattraction));
+
+console.log("displaying AdminAttractions")
+fetchAttractions()
+ .then(displayAdminAttractions)
diff --git a/client/src/adminArticle.js b/client/src/adminArticle.js
new file mode 100644
index 0000000..bc50915
--- /dev/null
+++ b/client/src/adminArticle.js
@@ -0,0 +1,117 @@
+import {
+ AdminAttraction
+} from "./templateImplementations.js"
+
+import {
+ kill,
+ findParent
+} from "./utils.js"
+
+export function addAttractionInDatabase(newattraction) {
+
+ return async function addAttraction(event) {
+ const name = newattraction.querySelector("#newattractionname").value;
+ const description = newattraction.querySelector("#newattractiondescription").value;
+ const lat = Number.parseFloat(newattraction.querySelector("#newlat").value);
+ const lon = Number.parseFloat(newattraction.querySelector("#newlong").value);
+ if (!name || !description) return;
+ if (Number.isNaN(lat)) return;
+ if (Number.isNaN(lon)) return;
+
+ console.log("adding " + name + " at lat: " + lat + " and long: " + lon);
+ try {
+ const response = await fetch("api/admin/add",
+ {
+ method: 'POST', // *GET, POST, PUT, DELETE, etc.
+ mode: 'cors', // no-cors, *cors, same-origin
+ cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
+ credentials: 'same-origin', // include, *same-origin, omit
+ headers: {
+ 'Content-Type': 'application/json'
+ // 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ redirect: 'follow', // manual, *follow, error
+ referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
+ body: JSON.stringify({name: name, description: description, lat: lat, lon: lon}) // body data type must match "Content-Type" header
+ });
+
+ if (!response.ok) {
+ const message = `An error has occured: ${response.status}`;
+ throw new Error(message);
+ }
+
+ const main = findParent(parent => {return parent.tagName === "MAIN"})(event.target)
+ const template = main.parentNode.querySelector("#adminattraction");
+ const attractionJSON = await response.json();
+ console.log(attractionJSON)
+ const newAttraction = new AdminAttraction(attractionJSON, template);
+ newAttraction.addToNode(main);
+
+ } catch(error) {
+ console.log("Something went wrong when deleting an attraction: ", error);
+ }
+
+ }
+}
+
+export function deleteAttractionInDatabase(name) {
+ return async function deleteAttraction(event) {
+ console.log("deleting " + name);
+ try {
+ const response = await fetch("api/admin/delete",
+ {
+ method: 'POST', // *GET, POST, PUT, DELETE, etc.
+ mode: 'cors', // no-cors, *cors, same-origin
+ cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
+ credentials: 'same-origin', // include, *same-origin, omit
+ headers: {
+ 'Content-Type': 'application/json'
+ // 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ redirect: 'follow', // manual, *follow, error
+ referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
+ body: JSON.stringify({name: name}) // body data type must match "Content-Type" header
+ });
+
+ if (!response.ok) {
+ const message = `An error has occured: ${response.status}`;
+ throw new Error(message);
+ }
+
+ } catch(error) {
+ console.log("Something went wrong when deleting an attraction: ", error);
+ }
+ kill(findParent(parent => {return parent.tagName === "ARTICLE"})(event.target));
+ }
+}
+
+
+export function updateAttractionInDatabase(name, field) {
+ return async function update(event) {
+ console.log("updating " + field + " field of " + name);
+ try {
+ const response = await fetch("api/admin/edit",
+ {
+ method: 'POST', // *GET, POST, PUT, DELETE, etc.
+ mode: 'cors', // no-cors, *cors, same-origin
+ cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
+ credentials: 'same-origin', // include, *same-origin, omit
+ headers: {
+ 'Content-Type': 'application/json'
+ // 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ redirect: 'follow', // manual, *follow, error
+ referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
+ body: JSON.stringify({name: name, field: field, value: event.target.value}) // body data type must match "Content-Type" header
+ });
+
+ if (!response.ok) {
+ const message = `An error has occured: ${response.status}`;
+ throw new Error(message);
+ }
+
+ } catch(error) {
+ console.log("Something went wrong when editing an attraction: ", error);
+ }
+ }
+}
diff --git a/client/src/attractionArticle.js b/client/src/attractionArticle.js
new file mode 100644
index 0000000..493417e
--- /dev/null
+++ b/client/src/attractionArticle.js
@@ -0,0 +1,253 @@
+import {
+ fetchAttractions,
+ findParent,
+ dutchCurrencyFormat,
+ dutchCurrencyFormatWithSign,
+ displayNumberOfItemsInShoppingBasketWithBadge
+} from "./utils.js"
+
+export function disableButton(name, button) {
+ return function receiver(payload) {
+ console.log('checking if button should be disabled');
+ 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"));
+ // console.log(shoppingBasketArray);
+ var shoppingBasketTickets = 0;
+
+ if (shoppingBasketArray) {
+ for (let i = 0; i < shoppingBasketArray.length; i++) {
+ if (shoppingBasketArray[i].name === name) {
+ shoppingBasketTickets = shoppingBasketTickets + shoppingBasketArray[i].numberOfKids + shoppingBasketArray[i].numberOfAdults;
+ }
+ }
+ }
+ const totalTickets = inputTickets + shoppingBasketTickets;
+
+ function disablerHelper(payload) {
+ var attraction;
+ if (Array.isArray(payload)) {
+ for (let i = 0; i < payload.length; i++) {
+ if (payload[i].name === name) {
+ attraction = payload[i];
+ }
+ }
+ } else attraction = payload;
+
+ var front = button.querySelector(".front");
+ if (attraction.available < totalTickets || attraction.available === 0 || attraction.available === shoppingBasketTickets) {
+ console.log("---> disabling " + attraction.name + " button since the available tickets are " + attraction.available + " and tickets among current order is " + totalTickets);
+ front.classList.add("disabled");
+ button.removeEventListener("click", orderButtonClicked);
+ } else {
+ console.log("---> enabling " + attraction.name + " button since the available tickets are " + attraction.available + " and tickets among current order is " + totalTickets);
+ front.classList.remove("disabled");
+ button.addEventListener("click", orderButtonClicked);
+ }
+
+ }
+
+ // console.log(payload)
+ if (payload && payload.name) {
+ if (payload.name) {
+ // console.log("prevented unnecesary fetch")
+ disablerHelper(payload);
+ }
+ } else {
+ // console.log("unnecesary fetch")
+ fetchAttractions()
+ .then(disablerHelper)
+ .catch(error => {console.error(error)});
+ }
+ }
+}
+
+export function orderButtonClicked(event) {
+ console.log("button click");
+ var button;
+
+ if (event.target.classList.contains("orderbutton")) {
+ button = event.target;
+ } else {
+ button = event.target.parentNode;
+ }
+
+ 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("---> found this info on the client side:")
+ 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.error(error)})
+ }
+}
+
+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 (attraction.available < orderClientSideInfo.numberOfKids + orderClientSideInfo.numberOfAdults) {
+ throw new TicketsNotAvailableError("The tickets of the order exceed the available tickets!");
+ }
+
+ return serverAttractionsArray;
+ }
+}
+
+export function saveOrderInShoppingBasket(orderClientSideInfo) {
+ console.log("---> ---> saving info in shopping basket");
+
+ return function serverAttractionsAccepter(serverAttractionsArray) {
+ // const orderClientSideInfo = this;
+
+ var price;
+ var attraction;
+ for (let i = 0; i < serverAttractionsArray.length; i++) {
+ if (serverAttractionsArray[i].name === orderClientSideInfo.name) {
+ price = calulateTotal(
+ orderClientSideInfo.numberOfKids,
+ orderClientSideInfo.numberOfAdults,
+ serverAttractionsArray[i]
+ );
+ attraction = serverAttractionsArray[i];
+ }
+ }
+
+ orderClientSideInfo.price = price;
+ console.log("---> ---> price is saved in the shopping basket order");
+ console.log(orderClientSideInfo);
+
+ var shoppingBasketArray;
+
+ if (localStorage.getItem("shoppingBasketArray") === null) {
+ shoppingBasketArray = [];
+ shoppingBasketArray.push(orderClientSideInfo);
+ } else {
+ shoppingBasketArray = JSON.parse(localStorage.getItem("shoppingBasketArray"));
+ shoppingBasketArray.push(orderClientSideInfo);
+ }
+
+ localStorage.setItem("shoppingBasketArray", JSON.stringify(shoppingBasketArray));
+ console.log("---> ---> order is saved in array in localstorage");
+ console.log(localStorage);
+ displayNumberOfItemsInShoppingBasketWithBadge();
+ return attraction;
+ }
+}
+
+export function calulateTotal(numberOfKids, numberOfAdults, serverSideAttraction) {
+ console.log("---> calculating total of order in shoppingbasket!");
+ console.log("---> ---> fetched this attraction to calculate actual prices!");
+ 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;
+ }
+
+ if (numberOfKids >= minNumberkids && numberOfAdults >= minNumberAdults) {
+ var discount = totalPrice * discountPercentage / 100;
+ totalPrice = totalPrice - discount;
+ }
+
+ if (discount) {
+ return {total:totalPrice, discount: discount}
+ } else {
+ return {total: totalPrice};
+ }
+}
+
+export function displayTotal(event) {
+ console.log("displaying a total price based on client side info");
+ var order = findParent(parent => {return parent.classList.contains("order")})(event.target)
+ console.log(order)
+ var total = order.querySelector(".total");
+
+ var kids = order.querySelector(".numberofkids").value;
+ var adults = order.querySelector(".numberofadults").value;
+ if (kids === "") kids = 0;
+ if (adults === "") adults = 0;
+
+ 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("---> applying the discount of " + discountPercentage + "%, resulting in " + discount + " discount")
+ }
+ // console.log(value);
+
+ var priceString = dutchCurrencyFormat(value);
+ if (!(discount === undefined)) {
+ priceString = priceString + " discount: " + dutchCurrencyFormatWithSign(discount);
+ }
+ total.querySelector(".price").textContent = priceString;
+}
diff --git a/client/src/functions.js b/client/src/functions.js
index a43dbac..e69de29 100644
--- a/client/src/functions.js
+++ b/client/src/functions.js
@@ -1,21 +0,0 @@
-// client/src/function.js
-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 = await 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 0b8e676..5743b5e 100644
--- a/client/src/index.js
+++ b/client/src/index.js
@@ -1,11 +1,13 @@
import { ParkArticle } from "./templateImplementations.js";
+
import {
+ fetchAttractions,
displayNumberOfItemsInShoppingBasketWithBadge,
dutchCurrencyFormat,
dutchCurrencyFormatWithSign,
- findParent
+ findParent,
+ killChildren,
} from "./utils.js";
-import { fetchAttractions } from "./functions.js"
// ik struggle een beetje met de database connecten met de api...
@@ -14,266 +16,14 @@ import { fetchAttractions } from "./functions.js"
function displayArticles(articles) {
console.log("displaying attraction 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 receiver(payload) {
- console.log('checking if button should be disabled');
- 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"));
- // console.log(shoppingBasketArray);
- var shoppingBasketTickets = 0;
-
- if (shoppingBasketArray) {
- for (let i = 0; i < shoppingBasketArray.length; i++) {
- if (shoppingBasketArray[i].name === name) {
- shoppingBasketTickets = shoppingBasketTickets + shoppingBasketArray[i].numberOfKids + shoppingBasketArray[i].numberOfAdults;
- }
- }
- }
- const totalTickets = inputTickets + shoppingBasketTickets;
-
- function disablerHelper(payload) {
- var attraction;
- if (Array.isArray(payload)) {
- for (let i = 0; i < payload.length; i++) {
- if (payload[i].name === name) {
- attraction = payload[i];
- }
- }
- } else attraction = payload;
-
- var front = button.querySelector(".front");
- if (attraction.available < totalTickets || attraction.available === 0 || attraction.available === shoppingBasketTickets) {
- console.log("---> disabling " + attraction.name + " button since the available tickets are " + attraction.available + " and tickets among current order is " + totalTickets);
- front.classList.add("disabled");
- button.removeEventListener("click", orderButtonClicked);
- } else {
- console.log("---> enabling " + attraction.name + " button since the available tickets are " + attraction.available + " and tickets among current order is " + totalTickets);
- front.classList.remove("disabled");
- button.addEventListener("click", orderButtonClicked);
- }
-
- }
-
- // console.log(payload)
- if (payload && payload.name) {
- if (payload.name) {
- // console.log("prevented unnecesary fetch")
- disablerHelper(payload);
- }
- } else {
- // console.log("unnecesary fetch")
- fetchAttractions()
- .then(disablerHelper)
- .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;
- }
-
- 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("---> found this info on the client side:")
- 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.error(error)})
- }
-}
-
-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 (attraction.available < orderClientSideInfo.numberOfKids + orderClientSideInfo.numberOfAdults) {
- throw new TicketsNotAvailableError("The tickets of the order exceed the available tickets!");
- }
-
- return serverAttractionsArray;
- }
-}
-
-function saveOrderInShoppingBasket(orderClientSideInfo) {
- console.log("---> ---> saving info in shopping basket");
-
- return function serverAttractionsAccepter(serverAttractionsArray) {
- // const orderClientSideInfo = this;
-
- var price;
- var attraction;
- for (let i = 0; i < serverAttractionsArray.length; i++) {
- if (serverAttractionsArray[i].name === orderClientSideInfo.name) {
- price = calulateTotal(
- orderClientSideInfo.numberOfKids,
- orderClientSideInfo.numberOfAdults,
- serverAttractionsArray[i]
- );
- attraction = serverAttractionsArray[i];
- }
- }
-
- orderClientSideInfo.price = price;
- console.log("---> ---> price is saved in the shopping basket order");
- console.log(orderClientSideInfo);
-
- var shoppingBasketArray;
-
- if (localStorage.getItem("shoppingBasketArray") === null) {
- shoppingBasketArray = [];
- shoppingBasketArray.push(orderClientSideInfo);
- } else {
- shoppingBasketArray = JSON.parse(localStorage.getItem("shoppingBasketArray"));
- shoppingBasketArray.push(orderClientSideInfo);
- }
-
- localStorage.setItem("shoppingBasketArray", JSON.stringify(shoppingBasketArray));
- console.log("---> ---> order is saved in array in localstorage");
- console.log(localStorage);
- displayNumberOfItemsInShoppingBasketWithBadge();
- return attraction;
- }
-}
-
-function calulateTotal(numberOfKids, numberOfAdults, serverSideAttraction) {
- console.log("---> calculating total of order in shoppingbasket!");
- console.log("---> ---> fetched this attraction to calculate actual prices!");
- 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;
- }
-
- if (numberOfKids >= minNumberkids && numberOfAdults >= minNumberAdults) {
- var discount = totalPrice * discountPercentage / 100;
- totalPrice = totalPrice - discount;
+ parkArticle.addToNode(document.querySelector("#center-articles"));
}
- if (discount) {
- return {total:totalPrice, discount: discount}
- } else {
- return {total: totalPrice};
- }
}
-function displayTotal(event) {
- console.log("displaying a total price based on client side info");
- var order = findParent(parent => {return parent.classList.contains("order")})(event.target)
- console.log(order)
- var total = order.querySelector(".total");
-
- var kids = order.querySelector(".numberofkids").value;
- var adults = order.querySelector(".numberofadults").value;
- if (kids === "") kids = 0;
- if (adults === "") adults = 0;
-
- 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("---> applying the discount of " + discountPercentage + "%, resulting in " + discount + " discount")
- }
- // console.log(value);
-
- var priceString = dutchCurrencyFormat(value);
- if (!(discount === undefined)) {
- priceString = priceString + " discount: " + dutchCurrencyFormatWithSign(discount);
- }
- total.querySelector(".price").textContent = priceString;
-}
-
-
function setStickyNavBar() {
console.log("making the navbar sticky");
// Get the header
@@ -301,5 +51,9 @@ function setStickyNavBar() {
displayNumberOfItemsInShoppingBasketWithBadge();
setStickyNavBar();
+const sortmenu = document.querySelector("#sortmenu");
+
+console.log(sortmenu)
+
fetchAttractions()
.then(displayArticles);
diff --git a/client/src/map.js b/client/src/map.js
index 10f2d66..6af7619 100644
--- a/client/src/map.js
+++ b/client/src/map.js
@@ -1,4 +1,4 @@
-import { fetchAttractions } from "./functions.js"
+import { fetchAttractions } from "./utils.js"
import { ParkArticle } from "./templateImplementations.js"
// replace "toner" here with "terrain" or "watercolor"
@@ -10,6 +10,8 @@ var map = new L.Map("discoverablemap", {
map.addLayer(layer);
function addMarkersForAttractions(map) {
+ const template = document.querySelector("#parkpopup");
+
return function addMarkers(attractions) {
for (let i = 0; i < attractions.length; i++) {
const attraction = attractions[i];
@@ -17,20 +19,14 @@ function addMarkersForAttractions(map) {
const marker = L.marker([location.lat, location.lon]).addTo(map);
- const articleObj = new ParkArticle(attraction, document.querySelector("parkpopup"))
- console.log(articleObj)
- const parkArticleFunctionality = {
- orderButtonClick: orderButtonClicked,
- displayTotal: displayTotal,
- disableButton: disableButton,
- }
- const articleHTML = articleObj.toHTML(parkArticleFunctionality);
- console.log(articleHTML);
+ const articleObj = new ParkArticle(attraction, template);
+ const articleHTML = articleObj.toHTML();
+ var div = L.DomUtil.create('div', 'popupcontent');
+ const popupcontent = articleHTML;
+ div.appendChild(popupcontent);
- marker.bindPopup(
- articleHTML
- )
+ marker.bindPopup(div)
}
}
diff --git a/client/src/myorders.js b/client/src/myorders.js
index e69de29..302cb55 100644
--- a/client/src/myorders.js
+++ b/client/src/myorders.js
@@ -0,0 +1,24 @@
+import { Order } from "./templateImplementations.js"
+import {
+ displayNumberOfItemsInShoppingBasketWithBadge,
+ fetchOrders,
+} from "./utils.js";
+
+
+async function displayOrders() {
+ displayNumberOfItemsInShoppingBasketWithBadge();
+ console.log("displaying orders in database");
+
+ var orders = await fetchOrders();
+ if (orders === null || orders.length === 0) {
+ }
+
+ var main = document.querySelector("main");
+
+ for (let i = 0; i < orders.length; i++) {
+ var orderObj = new Order(orders[i], document.querySelector("#ticket"));
+ orderObj.addToNode(main);
+ }
+}
+
+displayOrders();
diff --git a/client/src/orderArticle.js b/client/src/orderArticle.js
new file mode 100644
index 0000000..be5a75b
--- /dev/null
+++ b/client/src/orderArticle.js
@@ -0,0 +1,34 @@
+import {
+ fetchOrders,
+ readOrderArrayFromLocalStorage,
+ kill,
+ findParent,
+ displayNumberOfItemsInShoppingBasketWithBadge
+} from "./utils.js"
+
+export function cancelOrder(event) {
+ //console.log(event.target);
+ console.log("cancel button clicked");
+ const article = findParent(parent => {return parent.tagName === "ARTICLE"})(event.target);
+
+ var previous = article.previousSibling;
+ var i = 0;
+ while (previous) {
+ if (previous.tagName === "ARTICLE") {
+ i = i+1;
+ }
+ previous = previous.previousSibling;
+ }
+
+ var orders = readOrderArrayFromLocalStorage();
+ console.log("---> canceling order ");
+ console.log(orders[i]);
+ console.log("---> removing it from shoppingbasket")
+ orders.splice(i, 1);
+ localStorage.setItem("shoppingBasketArray", JSON.stringify(orders));
+
+ var main = document.querySelector("main");
+ console.log("---> refreshing displayed orders")
+ kill(article);
+ displayNumberOfItemsInShoppingBasketWithBadge();
+}
diff --git a/client/src/shoppingbasket.js b/client/src/shoppingbasket.js
index 56e717a..434b8cf 100644
--- a/client/src/shoppingbasket.js
+++ b/client/src/shoppingbasket.js
@@ -1,20 +1,15 @@
import { Order } from "./templateImplementations.js"
-import { displayNumberOfItemsInShoppingBasketWithBadge, findParentWithTag, childKillerUsingTags } from "./utils.js";
+import {
+ displayNumberOfItemsInShoppingBasketWithBadge,
+ readOrderArrayFromLocalStorage,
+} from "./utils.js";
-function getOrderArray() {
- var orders = JSON.parse(localStorage.getItem("shoppingBasketArray"));
- return orders;
-}
-
function displayOrders() {
displayNumberOfItemsInShoppingBasketWithBadge();
console.log("displaying orders in shopppingbasket");
- const orderFunctionality = {
- cancel: cancelOrder,
- };
- var orders = getOrderArray();
+ var orders = readOrderArrayFromLocalStorage();
if (orders === null || orders.length === 0) {
var button = document.querySelector("#finalizepaymentbutton");
var front = button.querySelector(".front");
@@ -30,40 +25,10 @@ function displayOrders() {
for (let i = 0; i < orders.length; i++) {
var orderObj = new Order(orders[i], document.querySelector("#ticket"));
- orderObj.addToNode(main, orderFunctionality);
+ orderObj.addToNode(main);
}
}
-function cancelOrder(event) {
- //console.log(event.target);
- console.log("cancel button clicked");
- const article = findParentWithTag.bind(event.target)("article");
-
- var previous = article.previousSibling;
- var i = 0;
- while (previous) {
- if (previous.tagName === "ARTICLE") {
- i = i+1;
- }
- previous = previous.previousSibling;
- }
-
- var orders = getOrderArray();
- console.log("---> canceling order ");
- console.log(orders[i]);
- console.log("---> removing it from shoppingbasket")
- orders.splice(i, 1);
- localStorage.setItem("shoppingBasketArray", JSON.stringify(orders));
-
- var main = document.querySelector("main");
- console.log("---> refreshing displayed orders")
- childKillerUsingTags(main)(main.firstChild)("article");
- displayOrders();
-}
-
-
-
-
function finalizePayment(event) {
console.log("finalizing payments");
@@ -71,8 +36,6 @@ function finalizePayment(event) {
window.location.replace("orderplaced.html");
}
-
-
document.querySelector("#finalizepaymentbutton").addEventListener("click", finalizePayment);
displayOrders();
diff --git a/client/src/templateImplementations.js b/client/src/templateImplementations.js
index 5767a84..5940d7e 100644
--- a/client/src/templateImplementations.js
+++ b/client/src/templateImplementations.js
@@ -1,5 +1,24 @@
-import { dutchCurrencyFormat, dutchCurrencyFormatWithSign } from "./utils.js"
-import { fetchAttractions } from "./functions.js"
+import {
+ dutchCurrencyFormat,
+ dutchCurrencyFormatWithSign,
+} from "./utils.js"
+
+import {
+ disableButton,
+ calulateTotal,
+ checkTicketAvailability,
+ displayTotal
+} from "./attractionArticle.js"
+
+import {
+ cancelOrder
+} from "./orderArticle.js"
+
+import {
+ updateAttractionInDatabase,
+ deleteAttractionInDatabase
+} from "./adminArticle.js"
+
/**
* Abstract class
*
@@ -21,10 +40,38 @@ class TemplatedNode {
}
}
+export class AdminAttraction extends TemplatedNode {
+ addToNode(node) {
+ var clone = this.template.content.cloneNode(true);
+ clone.querySelector(".parkname").textContent = this.name;
+
+ clone.querySelector(".adultprice").querySelector(".adultPrice").value = this.adultPrice;
+ clone.querySelector(".kidsprice").querySelector(".kidsPrice").value = this.kidsPrice;
+
+ clone.querySelector(".discountrequirement").querySelector(".minimumNumberOfAdults").value = this.minimumNumberOfAdults;
+ clone.querySelector(".discountrequirement").querySelector(".minimumNumberOfKids").value = this.minimumNumberOfKids;
+ clone.querySelector(".discountrequirement").querySelector(".discount").value = this.discount;
+
+ clone.querySelector(".availabletickets").querySelector(".available").value = this.available;
+
+ const inputElements = clone.querySelectorAll("input");
+ for (let i = 0; i < inputElements.length; i++) {
+ const input = inputElements[i];
+ const fieldToUpdate = input.className;
+ input.addEventListener("input", updateAttractionInDatabase(this.name, fieldToUpdate));
+ }
+
+ const deletebutton = clone.querySelector("#deletebutton");
+ deletebutton.addEventListener("click", deleteAttractionInDatabase(this.name));
+
+ node.appendChild(clone);
+ }
+}
+
export class Order extends TemplatedNode {
- addToNode(node, orderFunctionality) {
+ addToNode(node) {
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template
var clone = this.template.content.cloneNode(true);
@@ -40,7 +87,9 @@ export class Order extends TemplatedNode {
}
clone.querySelector(".price").textContent = priceString;
- clone.querySelector("button").addEventListener("click", orderFunctionality.cancel);
+ var button = clone.querySelector("button");
+
+ if (button) button.addEventListener("click", cancelOrder);
node.appendChild(clone);
}
@@ -49,17 +98,17 @@ export class Order extends TemplatedNode {
export class ParkArticle extends TemplatedNode {
- addToNode(node, parkArticleFunctionality) {
- const clone = this.cloneTemplateAndFillInHTML(parkArticleFunctionality);
+ addToNode(node) {
+ const clone = this.cloneTemplateAndFillInHTML();
node.appendChild(clone);
}
- toHTML(parkArticleFunctionality) {
- return this.cloneTemplateAndFillInHTML(parkArticleFunctionality);
+ toHTML() {
+ return this.cloneTemplateAndFillInHTML();
}
- cloneTemplateAndFillInHTML(parkArticleFunctionality) {
+ cloneTemplateAndFillInHTML() {
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template
var clone = this.template.content.cloneNode(true);
@@ -94,12 +143,12 @@ export class ParkArticle extends TemplatedNode {
}
var button = clone.querySelector(".orderbutton");
- parkArticleFunctionality.disableButton(this.name, button)(null);
+ 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));
+ inputElements[i].addEventListener("input", displayTotal);
+ inputElements[i].addEventListener("input", disableButton(this.name, button));
}
return clone;
}
diff --git a/client/src/utils.js b/client/src/utils.js
index 5a0423d..b58da6c 100644
--- a/client/src/utils.js
+++ b/client/src/utils.js
@@ -1,3 +1,52 @@
+// client/src/function.js
+export async function fetchOrders() {
+
+ try {
+
+ const response = await fetch("api/myorders");
+
+ if (!response.ok) {
+ const message = `An error has occured: ${response.status}`;
+ throw new Error(message);
+ }
+
+ const orders = await response.json();
+ return orders;
+
+
+ } catch(error) {
+ console.log("something went wrong when fetching orders: ", error);
+ }
+
+}
+
+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 = await response.json();
+ return attractions;
+
+
+ } catch(error) {
+ console.log("something went wrong when fetching attractions: ", error);
+ }
+
+}
+
+export function readOrderArrayFromLocalStorage() {
+ var orders = JSON.parse(localStorage.getItem("shoppingBasketArray"));
+ return orders;
+}
+
+
export function displayNumberOfItemsInShoppingBasketWithBadge() {
var shoppingBasketArray = JSON.parse(localStorage.getItem("shoppingBasketArray"));
if (shoppingBasketArray === null) {
@@ -20,14 +69,6 @@ export function dutchCurrencyFormatWithSign(number) {
}
-export function findParentWithTag(tagName) {
- if (this.tagName === tagName.toUpperCase()) {
- return this;
- } else {
- return findParentWithTag.bind(this.parentNode)(tagName);
- }
-}
-
export function findParent(func) {
return function startingFromThisNode(node) {
if (func(node)) {
@@ -38,22 +79,12 @@ export function findParent(func) {
}
}
+export function kill(node) {
+ node.parentNode.removeChild(node);
+}
-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);
- }
- }
-
+export function killChildren(func) {
+ return function startingFromThisChildNode(node) {
}
}
+
diff --git a/client/style/admin.css b/client/style/admin.css
new file mode 100644
index 0000000..c2d3f0e
--- /dev/null
+++ b/client/style/admin.css
@@ -0,0 +1,29 @@
+.info {
+ margin: 15px;
+}
+
+input {
+ width: 50px;
+}
+
+.newattraction {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: left;
+ align-items: center;
+ margin: 15px 2.3% 15px 2.3%;
+ border: 1px solid var(--primary-color);
+}
+
+.newattractioninput {
+ margin: 5px;
+ width: auto;
+}
+
+.newattractioninput input {
+ min-width: 140px;
+ resize: both;
+ height: 30px;
+}
+
diff --git a/client/style/main.css b/client/style/main.css
index 0159545..abebf9c 100644
--- a/client/style/main.css
+++ b/client/style/main.css
@@ -28,7 +28,7 @@ article {
border: 1px solid var(--primary-color);
color: var(--light-text-color);
width: 96%;
- margin: 2% 0 0 2%;
+ margin: 2% 0 10px 2%;
}
#logo-header {
display: none;
@@ -117,7 +117,7 @@ article {
border-radius: 25px;
border: 1px solid var(--primary-color);
color: var(--light-text-color);
- margin: 10px 0 0 2.3%;
+ margin: 10px 0 10px 2.3%;
display: inline-block;
width: 30%;
position: relative;
@@ -302,3 +302,28 @@ main {
font-size: 1rem;
padding: 6px 15px;
}
+
+.orderdescription {
+ display: block;
+ text-align: center;
+ margin: 5px;
+}
+
+#sortmenu {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: center;
+ align-items: center;
+ margin: 15px 2.3% 15px 2.3%;
+ border: 1px solid var(--primary-color);
+}
+
+#sortmenu .sorter {
+ width: 50%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: row;
+ flex-wrap: wrap;
+}
diff --git a/client/style/map.css b/client/style/map.css
index e64a4cd..3d9dd1f 100644
--- a/client/style/map.css
+++ b/client/style/map.css
@@ -15,3 +15,122 @@ main {
position: relative;
height:100%;
}
+
+/* Describes the styling of all <article> elements */
+article {
+ color: var(--light-text-color);
+ border: none;
+ display: block;
+ width: 100%;
+ position: relative;
+}
+
+/* Describes the styling of all elements with class="parkname", note the . in front of parkname */
+.parkname {
+ background-color: white;
+ color: black;
+ font-size: 1rem;
+ font-weight: bold;
+ text-transform: none;
+ display: block;
+ height: auto;
+}
+
+/* Describes the styling of all elements with class="parkname", note the . in front of parkname */
+.parkdescription {
+ font-size: 1rem;
+ text-align: left;
+ height: 30px;
+ margin: 15px 15px 0 15px;
+}
+
+.order {
+ padding-top: 25px;
+ text-align:center;
+}
+
+.prices {
+ margin-bottom: 15px;
+ margin-left: 15px;
+ margin-right: 15px;
+ font-size: 0.9rem;
+}
+
+.total {
+ margin: 10px;
+ font-size: 0.9rem;
+}
+
+.adultinput {
+ width:100%;
+}
+
+.kidsinput {
+ width:100%;
+}
+
+.orderbutton {
+ background: hsl(170deg 0% 64%);
+ border-radius: 12px;
+ border: none;
+ padding: 0;
+ cursor: pointer;
+ outline-offset: 4px;
+ margin: 20px;
+}
+
+.front {
+ font-size: 1rem;
+ padding: 6px 15px;
+
+ padding: 10px 9px;
+ font-size: 1.2rem;
+
+ display: block;
+ border-radius: 12px;
+ background: var(--primary-color);
+ color: white;
+ transform: translateY(-4px);
+ transition:
+ transform 600ms
+ cubic-bezier(.3, .7, .4, 1);
+}
+
+.disabled {
+ display: block;
+ border-radius: 12px;
+ background: grey;
+ color: white;
+ transform: translateY(-2px);
+ padding: 10px 9px;
+ font-size: 1.2rem;
+}
+
+.orderbutton:hover .front {
+ transform: translateY(-6px);
+ transition:
+ transform
+ 250ms
+ cubic-bezier(.3, .7, .4, 1.5);
+}
+
+
+.orderbutton:active .front {
+ transform: translateY(-2px);
+}
+
+.orderbutton:active .disabled {
+ transform: translateY(-2px);
+}
+
+.orderbutton:hover .disabled {
+ transform: translateY(-2px);
+}
+
+
+
+
+.cancel .front {
+ font-size: 1rem;
+ padding: 6px 15px;
+}