diff --git a/content/master.blade.php b/content/master.blade.php index 2ab14c5..0997c14 100644 --- a/content/master.blade.php +++ b/content/master.blade.php @@ -62,7 +62,6 @@ -
+
diff --git a/content/pages/checkout.blade.php b/content/pages/checkout.blade.php index 30b8aa4..5535f91 100644 --- a/content/pages/checkout.blade.php +++ b/content/pages/checkout.blade.php @@ -163,7 +163,7 @@ @@ -190,7 +190,7 @@
- + Warenkorb teilen @@ -204,5 +204,5 @@ @section('scripts') - + @stop diff --git a/js/checkout.js b/js/checkout.js index a35c99b..663d44a 100644 --- a/js/checkout.js +++ b/js/checkout.js @@ -1,17 +1,22 @@ +import * as Cart from './modules/cart.js'; +import { numberToEuroFormat } from './utils.js'; +import { Product } from './elements/product.js'; + function clearAddressFields() { - $('#address-components').hide(); - $('#street-number').val(''); - $('#street-number').prop('disabled', true); - $('#route').val(''); - $('#locality').val(''); - $('#postal-code').val(''); + document.querySelector('#address-components').style.display = "none"; + document.querySelector('#street-number').value = ''; + document.querySelector('#street-number').setAttribute('disabled', true); + document.querySelector('#route').value = ''; + document.querySelector('#locality').value = ''; + document.querySelector('#postal-code').value = ''; } + function getAddressFiedlValue(addressComponents, $field) { let field = addressComponents.find(obj => obj.types.indexOf($field) > -1); return field != undefined ? field.long_name : ''; } -urlBase = $('meta[name="url-base"]').attr("content"); +const urlBase = document.querySelector('meta[name="url-base"]').getAttribute("content"); let input = document.querySelector("#ctelefon"); let iti = null; @@ -28,50 +33,55 @@ if (input != undefined) { utilsScript: urlBase + "/template/libs/intl-tel-input/js/utils.min.js", }); } -function updateProducts() { - $('#list-products').html(''); - if (objProducts.length > 0) { - getInfoFromSelectedProduct().done(function (response, status) { - response.contents.forEach(function (product) { - let objProduct = getProductInfo(product); - let elementProduct = createProductElement(objProduct); - $('#list-products').append(elementProduct); - }); - if (totalPrice > 0) { - $('#total-checkout-price').text(numberToEuroFormat(totalPrice)); - } else { - $('#form-section').hide(); - $('#list-products').parent().parent().parent().parent().parent().find('.modal-footer').hide(); - $('#total-checkout-price').parent().parent().parent().hide(); - $('#no-products-in-checkout').hide(); - $('#cart-error-in-checkout').show(); - } - }).fail(function (response) { - $('#form-section').hide(); - $('#list-products').parent().parent().parent().parent().parent().find('.modal-footer').hide(); - $('#total-checkout-price').parent().parent().parent().hide(); - $('#no-products-in-checkout').hide(); - $('#cart-error-in-checkout').show(); + +async function updateProducts() { + document.querySelector('#list-products').innerHTML = ''; + + let products = await Cart.getItems(); + + if (products.length > 0) { + + products.forEach(function (product) { + + + let elementProduct = Product(product); + + let productRow = document.createElement('tr'); + productRow.innerHTML = elementProduct; + + document.querySelector('#list-products').append(productRow); + }); - $('#form-section').show(); - $('#list-products').parent().parent().parent().parent().parent().find('.modal-footer').show(); - $('#total-checkout-price').parent().parent().parent().show(); - $('#no-products-in-checkout').hide(); - $('#cart-error-in-checkout').hide(); + + if (Cart.getTotalPrice() > 0) { + document.querySelector('#total-checkout-price').text(numberToEuroFormat(Cart.getTotal())); + } else { + document.querySelector('#form-section').style.display = "none"; + //$('#list-products').parent().parent().parent().parent().parent().find('.modal-footer').hide(); + //document.querySelector('#total-checkout-price').parentElement.parentElement.parentElement.style.display = "none"; + document.querySelector('#no-products-in-checkout').style.display = "none"; + document.querySelector('#cart-error-in-checkout').style.display = "block"; + } + + document.querySelector('#form-section').style.display = "block"; + //document.querySelector('#list-products').parent().parent().parent().parent().parent().find('.modal-footer').show(); + //document.querySelector('#total-checkout-price').parentElement.parentElement.parentElement.style.display = "block"; + document.querySelector('#no-products-in-checkout').style.display = "none"; + document.querySelector('#cart-error-in-checkout').style.display = "none"; } else { - $('#form-section').hide(); - $('#list-products').parent().parent().parent().parent().parent().find('.modal-footer').hide(); - $('#total-checkout-price').parent().parent().parent().hide(); - $('#no-products-in-checkout').show(); - $('#cart-error-in-checkout').hide(); + document.querySelector('#form-section').style.display = "none"; + //$('#list-products').parent().parent().parent().parent().parent().find('.modal-footer').hide(); + document.querySelector('#total-checkout-price').parentElement.parentElement.parentElement.style.display = "none"; + document.querySelector('#no-products-in-checkout').style.display = "block"; + document.querySelector('#cart-error-in-checkout').style.display = "none"; } } -$(window).on('load', function () { +document.addEventListener('DOMContentLoaded', () => { updateProducts(); - $("#successful-submitting-form").delay(4000).slideUp(200, function () { $(this).alert('close'); }); - $('#anfrage').submit(function (event) { + //document.querySelector("#successful-submitting-form").delay(4000).slideUp(200, function () { $(this).alert('close'); }); + document.querySelector('#checkout').addEventListener("submit", function (event) { event.preventDefault(); let form = $(this); getInfoFromSelectedProduct().done(function (response, status) { @@ -88,32 +98,32 @@ $(window).on('load', function () { form.unbind('submit').submit(); }); }); - $(document).on('click', '.btn-remove-product', function () { - if (objProducts.length > 0) { - $('#form-section').show(); - $('#list-products').parent().parent().parent().parent().parent().find('.modal-footer').show(); - $('#total-checkout-price').parent().parent().parent().show(); - $('#no-products-in-checkout').hide(); - } else { - $('#form-section').hide(); - $('#list-products').parent().parent().parent().parent().parent().find('.modal-footer').hide(); - $('#total-checkout-price').parent().parent().parent().hide(); - $('#no-products-in-checkout').show(); + + document.body.addEventListener('click', async function (event) { + if (event.target.matches('.btn-remove-product')) { + if (objProducts.length > 0) { + $('#form-section').show(); + $('#list-products').parent().parent().parent().parent().parent().find('.modal-footer').show(); + $('#total-checkout-price').parent().parent().parent().show(); + $('#no-products-in-checkout').hide(); + } else { + $('#form-section').hide(); + $('#list-products').parent().parent().parent().parent().parent().find('.modal-footer').hide(); + $('#total-checkout-price').parent().parent().parent().hide(); + $('#no-products-in-checkout').show(); + } } }); - $(document).on('input', '.select-quantity', function () { - getInfoFromSelectedProduct().done(function (response, status) { - totalPrice = 0; - response.contents.forEach(function (product) { - let objProduct = getProductInfo(product); - objProduct = calculatePrice(objProduct); - totalPrice += objProduct.calc_preis * objProduct.quantity; - }); - $('#total-checkout-price').text(numberToEuroFormat(totalPrice)); - }); + + document.body.addEventListener('input', async function (event) { + if (event.target.matches('.select-quantity')) { + let products = await Cart.getTotalPrice(); + document.querySelector('.js-total-price').innerText = numberToEuroFormat(products); + } }); }); +/* $(window).on('load', google.maps.event, function () { let input = document.getElementById('field_location'); let options = { @@ -122,7 +132,9 @@ $(window).on('load', google.maps.event, function () { }; var autocomplete = new google.maps.places.Autocomplete(input, options); autocomplete.addListener("place_changed", () => { + clearAddressFields(); + const place = autocomplete.getPlace(); if (!place.geometry) return; var componentMap = { @@ -160,4 +172,5 @@ $(window).on('load', google.maps.event, function () { } }); }); -}); \ No newline at end of file +}); +*/ \ No newline at end of file diff --git a/js/elements/product.js b/js/elements/product.js new file mode 100644 index 0000000..113ce3c --- /dev/null +++ b/js/elements/product.js @@ -0,0 +1,29 @@ +import { numberToEuroFormat } from "../utils.js"; + +export function Product(data) { + return ` +
+ + + + `; +} \ No newline at end of file diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..093f7a3 --- /dev/null +++ b/js/main.js @@ -0,0 +1,199 @@ +import * as Cart from './modules/cart.js'; +import { numberToEuroFormat } from './utils.js'; +import { Product } from './elements/product.js'; + +async function displayProducts() { + document.querySelector('#list-products-in-bag').innerHTML = ''; + let products = await Cart.getItems(); + if (products.length > 0) { + products.forEach(function (product) { + let elementProduct = Product(product); + + let productRow = document.createElement("tr"); + productRow.innerHTML = elementProduct; + + document.querySelector('#list-products-in-bag').append(productRow); + }); + + let quantity = await Cart.getQuantity(); + document.querySelectorAll('.js-product-counter').forEach(element => { + element.innerText = quantity; + + if (quantity > 0) element.style.display = "block"; + else element.style.display = "none"; + }); + + let totalPrice = await Cart.getTotalPrice(); + document.querySelectorAll(".js-total-price").forEach(element => { + element.innerText = numberToEuroFormat(totalPrice); + }); + + let link = await Cart.getLink(); + document.querySelectorAll('.js-cart-link').forEach(element => element.value = link); + + if (navigator.share) document.querySelectorAll(".btn-share").forEach(button => { + button.addEventListener("click", async function () { + try { + const shareData = { + title: "Share cart list", + text: 'share cart list', + url: link, + }; + await navigator.share(shareData); + } + catch (error) { + console.error(error); + } + }); + }); + else { + document.querySelectorAll(".btn-share").forEach(button => { button.style.display = "none"; }); + + console.warn('Native Web Sharing not supported'); + } + + let copyCartLink = document.querySelector('#btn-copy-cart-link'); + + let copyIcon = ` + + + `; + + let copiedIcon = ` + + + + `; + + if (copyCartLink != undefined) copyCartLink.addEventListener("click", () => { + let cartLink = document.querySelector('.cart-link'); + if (cartLink && cartLink.select) { + navigator.clipboard.writeText(cartLink.value).then(() => { + copyCartLink.innerHTML = copiedIcon; + setTimeout(() => { copyCartLink.innerHTML = copyIcon; }, 3000); + }).catch(function (error) { + console.error(error); + }); + } + }); + + //document.querySelectorAll('.cart-link').forEach(cartLink => cartLink.style.display = "block"); + //document.querySelectorAll(".btn-share").forEach(btnShare => btnShare.style.display = "block"); + + //document.querySelector('#shopping-cart').querySelector('.modal-footer').style.display = "block"; + //document.querySelector('#total-price').parentElement.parentElement.parentElement.style.display = "block"; + document.querySelector('#no-products').style.display = "none"; + document.querySelector('#cart-error').style.display = "none"; + document.querySelector('#modal-buttons').style.display = "flex"; + } else { + document.querySelectorAll('.cart-link').forEach(cartLink => cartLink.style.display = "none"); + document.querySelectorAll(".btn-share").forEach(btnShare => btnShare.style.display = "none"); + + //document.querySelector('#list-products-in-bag').parentElement.parentElement.parentElement.parentElement.parentElement.querySelector('.modal-footer').style.display = "none"; + //document.querySelector('#total-price').parentElement.parentElement.parentElement.style.display = "none"; + document.querySelector('#no-products').style.display = "block"; + document.querySelector('#cart-error').style.display = "none"; + document.querySelector('#modal-buttons').style.display = "none"; + + document.querySelector('#selected-products').style.display = "none"; + } +} + +document.addEventListener('DOMContentLoaded', async () => { + + displayProducts(); + + document.body.addEventListener('click', async function (event) { + if (event.target.matches('.btn-remove-product')) { + let button = event.target; + + let productId = button.getAttribute('data-id'); + + await Cart.removeItem(productId); + + button.parentElement.parentElement.remove(); + + displayProducts(); + + /* productId = document.querySelector('meta[name="product-id"]').getAttribute('content'); + if (productId != undefined) { + let objProduct = objProducts.find(obj => obj.id == productId); + if (objProduct == undefined) { + $('#btn-add-to-bag').show(); + $('#btn-show-bag').hide(); + if (objProducts.length > 0) $('#btn-check-out').parent().hide(); + else $('#btn-check-out').parent().show(); + } + else { + $('#btn-add-to-bag').hide(); + $('#btn-show-bag').show(); + $('#btn-check-out').parent().hide(); + $('#count-items-in-bag').html(objProduct.quantity + ``); + } + } */ + } + }); + + /* document.querySelectorAll('.select-quantity').forEach(async input => { + + let quantity = input.value; + let productId = input.getAttribute('data-id'); + + Cart.updateItem(productId, { quantity: quantity }); + + let product = Cart.getItem(productId); + + let elementPrice = ` + ${numberToEuroFormat(product.calc_preis)}`; + if (product.calc_preis != product.preis) elementPrice += ` +
+ + ${(((product.calc_preis - product.preis) * 100) / product.preis).toFixed(2)} % + `; + input.parentElement.parentElement.querySelector('.product-price').innerHTML = elementPrice; + + quantity = await Cart.getQuantity(); + document.querySelectorAll('.js-product-counter').forEach(counter => { + counter.innerText = quantity; + if (quantity > 0) counter.style.display = "block"; + else counter.style.display = "none"; + }); + + document.querySelector('#total-price').innerText = numberToEuroFormat(Cart.getTotalPrice()); + + productId = document.querySelector('meta[name="product-id"]').getAttribute('content'); + if (productId != undefined) { + let objProduct = objProducts.find(obj => obj.id == productId); + if (objProduct == undefined) { + $('#btn-add-to-bag').show(); + $('#btn-show-bag').hide(); + if (objProducts.length > 0) $('#btn-check-out').parent().hide(); + else $('#btn-check-out').parent().show(); + } + else { + $('#btn-add-to-bag').hide(); + $('#btn-show-bag').show(); + $('#btn-check-out').parent().hide(); + $('#count-items-in-bag').html(objProduct.quantity + ``); + } + } +} ); * / + + /* let sharedCode = getSharedCode(); + if (sharedCode) { + if (Cart.getItems().length > 0) $('#modal-confirm-product-replacement').modal('show'); + else $('#modal-confirm-addition-of-products').modal('show'); + + $('.btn-confirm-product-link').click(function () { + objProducts = JSON.parse(sharedCode); + window.location.replace($('meta[name="checkout"]').attr('content')); + }); + } */ + + /* if (localStorage.getItem("dismiss-message") == 1) $('.alert-dismissible').remove(); + else $('.alert-dismissible').show(); + $('#btn-close-message').click(function () { localStorage.setItem("dismiss-message", 1); }); */ +}); diff --git a/js/modules/cart.js b/js/modules/cart.js new file mode 100644 index 0000000..e649bea --- /dev/null +++ b/js/modules/cart.js @@ -0,0 +1,97 @@ +import { url } from '../utils.js'; + +let isSync = false; +let items = null; + +export async function sync() { + if (!isSync) { + if (items !== null) { + let miniItems = items.map(obj => { return { id: obj.id, quantity: obj.quantity } }); + localStorage.setItem("cart", JSON.stringify(miniItems)); + } + + if (localStorage.getItem("cart") != null) { + let cartItems = localStorage.getItem("cart"); + items = JSON.parse(cartItems); + } + + let ids = items.map(obj => obj.id); + + if (ids.length > 0) { + let products = await fetch(document.querySelector('meta[name="get-contents"]').getAttribute('content') + "/" + ids.join(",")); + products = await products.json(); + + products.contents.forEach(product => { + product.quantity = items.find(obj => obj.id == product.id).quantity; + + product.calc_preis = product.preis; + if (product.quantity >= 5 && product.preis_5) product.calc_preis = product.preis_5; + if (product.quantity >= 10 && product.preis_10) product.calc_preis = product.preis_10; + }); + + items = products.contents; + } + + isSync = true; + } +} + +export function addItem(id) { + let objProductInfo = items.find(obj => obj.id == id); + let index = items.indexOf(objProductInfo); + if (index == -1) items.push({ id: id, quantity: 1 }); + isSync = false; + sync(); +} + +export async function updateItem(id, data) { + let product = await getItem(id); + if (data.quantity) product.quantity = data.quantity; + isSync = false; + await sync(); +} + +export async function removeItem(id) { + items = items.filter(item => item.id !== Number(id)); + console.log(items); + isSync = false; + await sync(); +} + +export async function getItem(id) { + await sync(); + return items.find(obj => obj.id == id); +} + +export async function getItems() { + await sync(); + return items; +} + +export async function getQuantity() { + await sync(); + let quantitys = items.map(obj => obj.quantity); + let quantityOfProducts = 0; + for (let i = 0; i < quantitys.length; i++) { if (Number.isInteger(Number(quantitys[i]))) quantityOfProducts += Number(quantitys[i]); } + return quantityOfProducts; +} + +export async function getTotalPrice() { + await sync(); + + let totalPrice = 0; + + items.forEach(product => { + product.calc_preis = product.preis; + if (product.quantity >= 5 && product.preis_5) product.calc_preis = product.preis_5; + if (product.quantity >= 10 && product.preis_10) product.calc_preis = product.preis_10; + totalPrice += product.calc_preis * product.quantity; + }); + + return totalPrice; +} + +export async function getLink() { + await sync(); + return url(btoa(JSON.stringify(items))); +} \ No newline at end of file diff --git a/js/product.js b/js/product.js index 3a9a4d0..da6a74f 100644 --- a/js/product.js +++ b/js/product.js @@ -1,20 +1,47 @@ -$(window).on('load', function(){ - $('#btn-check-out').click(function(){ - let productId = $(this).attr("data-id"); - addToBag(productId); - window.location.replace($('meta[name="checkout"]').attr('content')); - }); - $('#btn-add-to-bag').click(function(){ - let productId = $(this).attr("data-id"); - let parentElement = $('#count-products-in-bag').parent(); - let copyElemetn = $('#count-products-in-bag').clone(); - copyElemetn.attr('id', ''); - copyElemetn.appendTo(parentElement); - $('#count-products-in-bag').addClass('animate__animated animate__backInUp'); - $('#count-products-in-bag').on('animationend', function(){ - $('#count-products-in-bag').removeClass('animate__animated animate__backInUp'); - copyElemetn.remove(); - }); - addToBag(productId); +import * as Cart from './modules/cart.js'; + +document.addEventListener("DOMContentLoaded", async () => { + let productId = document.querySelector('meta[name="product-id"]').getAttribute('content'); + if (productId != undefined) { + + let products = await Cart.getItems(); + + let objProduct = products.find(obj => obj.id == productId); + if (objProduct == undefined) { + document.querySelector('#btn-add-to-bag').style.display = "block"; + document.querySelector('#btn-show-bag').style.display = "none"; + if (Cart.getItems().length > 0) document.querySelector('#btn-check-out').parentElement.style.display = "none"; + else document.querySelector('#btn-check-out').parentElement.display = "block"; + } + else { + $('#btn-add-to-bag').hide(); + $('#btn-show-bag').show(); + $('#btn-check-out').parent().hide(); + $('#count-items-in-bag').html(objProduct.quantity + ``); + } + } + + let btnCheckOut = document.querySelector('#btn-check-out'); + + btnCheckOut.addEventListener("click", () => { + let productId = btnCheckOut.getAttribute("data-id"); + Cart.addItem(productId); + window.location.replace(document.querySelector('meta[name="checkout"]').getAttribute('content')); + }); + + let btnAddToBag = document.querySelector('#btn-add-to-bag'); + + btnAddToBag.addEventListener("click", () => { + let productId = btnAddToBag.getAttribute("data-id"); + let parentElement = document.querySelector('#count-products-in-bag').parentElement; + let copyElemetn = document.querySelector('#count-products-in-bag').clone(); + copyElemetn.attr('id', ''); + copyElemetn.appendTo(parentElement); + $('#count-products-in-bag').addClass('animate__animated animate__backInUp'); + $('#count-products-in-bag').on('animationend', function () { + $('#count-products-in-bag').removeClass('animate__animated animate__backInUp'); + copyElemetn.remove(); }); + Cart.addProduct(productId); + }); }); \ No newline at end of file diff --git a/js/utils.js b/js/utils.js new file mode 100644 index 0000000..151cdc3 --- /dev/null +++ b/js/utils.js @@ -0,0 +1,54 @@ +export function numberToEuroFormat(number) { + let formatter = new Intl.NumberFormat('de', { style: 'currency', currency: 'EUR' }); + return formatter.format(number); +} + +export function isJson(string) { + try { JSON.parse(string); } + catch (error) { return false; } + return true; +} + +export function isEncoded(string) { + try { atob(string); } + catch (error) { return false; } + return true; +} + +export function fetchData(method = null, url, data = null) { + + const csrf = $('meta[name="csrf-token"]').attr('content'); + const urlBase = document.querySelector('meta[name="url-base"]').getAttribute("content"); + + const urlPattern = /^(https?:\/\/|ftp:\/\/)[^\s/$.?#].[^\s]*$/i; + const pathPattern = /^(\/|[^\/\s]+\/)[^\s]*$/; + + if (pathPattern.test(url)) url = urlBase + (url.startsWith('/') ? "" : "/") + url; + else if (!urlPattern.test(url)) throw new Error("Invalid URL."); + + return fetch(url, { + method: method ?? 'GET', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': csrf + }, + body: JSON.stringify(data ?? {}) + }) + .then(response => response.json()) + .catch(error => console.error("Error fetching data:", error)); +} + +export function url(path = "") { + let protocol = window.location.protocol; + let host = window.location.hostname; + let port = window.location.port; + let pathk = window.location.pathname; + + let baseUrl = protocol + "//" + host + (port ? ':' + port : ''); + + let pathArray = pathk.split('/'); + pathArray.pop(); + let directoryOnly = pathArray.join('/'); + + return baseUrl + directoryOnly + '/' + path; +} \ No newline at end of file
Gesammt: - 0 € + 0 €
Preise inkl. 0% Umsatzsteuer
+ + + + ${data.title} + +
+ sofort verfügbar +
+ + +
+ ${numberToEuroFormat(data.calc_preis)} +
+ + ${(((data.calc_preis - data.preis) * 100) / data.preis).toFixed(2) + ' %'} +