Compare commits

..

2 commits

Author SHA1 Message Date
ab65fa7a73 Kompatibilität zur alten JSON-Datei sowie kleinere Verbesserungen
Some checks failed
Deploy static content to Pages / deploy (push) Has been cancelled
2025-04-09 14:53:48 +02:00
97e3971f6f Hinzufügen einer Option zum Berechnen von Pfand 2025-04-09 14:47:51 +02:00
7 changed files with 90 additions and 12 deletions

View file

@ -3,60 +3,70 @@
"id": 75086086956949000, "id": 75086086956949000,
"name": "Wasser", "name": "Wasser",
"price": "0.5", "price": "0.5",
"deposit": null,
"image": "./assets/example/images/wasser.webp" "image": "./assets/example/images/wasser.webp"
}, },
{ {
"id": 403980494034923100, "id": 403980494034923100,
"name": "Cola", "name": "Cola",
"price": "1", "price": "1",
"deposit": "0.25",
"image": "./assets/example/images/cola.webp" "image": "./assets/example/images/cola.webp"
}, },
{ {
"id": 174111295271372860, "id": 174111295271372860,
"name": "Fanta", "name": "Fanta",
"price": "1", "price": "1",
"deposit": "0.25",
"image": "./assets/example/images/fanta.webp" "image": "./assets/example/images/fanta.webp"
}, },
{ {
"id": 13086165444583086, "id": 13086165444583086,
"name": "Bier", "name": "Bier",
"price": "1.5", "price": "1.5",
"deposit": "0.07",
"image": "./assets/example/images/bier.webp" "image": "./assets/example/images/bier.webp"
}, },
{ {
"id": 227920082494117630, "id": 227920082494117630,
"name": "Helles Bier", "name": "Helles Bier",
"price": "1.2", "price": "1.2",
"deposit": null,
"image": "./assets/example/images/bier_helles.webp" "image": "./assets/example/images/bier_helles.webp"
}, },
{ {
"id": 27617349687000040, "id": 27617349687000040,
"name": "Apfelsaft", "name": "Apfelsaft",
"price": "1.2", "price": "1.2",
"deposit": null,
"image": "./assets/example/images/apfelsaft.webp" "image": "./assets/example/images/apfelsaft.webp"
}, },
{ {
"id": 191783545996520450, "id": 191783545996520450,
"name": "Eistee", "name": "Eistee",
"price": "1.5", "price": "1.5",
"deposit": null,
"image": "./assets/example/images/eistee.webp" "image": "./assets/example/images/eistee.webp"
}, },
{ {
"id": 203321763516746460, "id": 203321763516746460,
"name": "Cider", "name": "Cider",
"price": "1.8", "price": "1.8",
"deposit": null,
"image": "./assets/example/images/cider.webp" "image": "./assets/example/images/cider.webp"
}, },
{ {
"id": 615216202376277900, "id": 615216202376277900,
"name": "Zitronenlimonade", "name": "Zitronenlimonade",
"price": "1", "price": "1",
"deposit": null,
"image": "./assets/example/images/limonade.webp" "image": "./assets/example/images/limonade.webp"
}, },
{ {
"id": 363150314621734500, "id": 363150314621734500,
"name": "Wodka Lemon", "name": "Wodka Lemon",
"price": "3", "price": "3",
"deposit": null,
"image": "./assets/example/images/wodka_lemon.webp" "image": "./assets/example/images/wodka_lemon.webp"
} }
] ]

View file

@ -3,60 +3,70 @@
"id": 75086086956949000, "id": 75086086956949000,
"name": "Wasser", "name": "Wasser",
"price": "0.5", "price": "0.5",
"deposit": null,
"image": "https://placehold.co/250x250?text=1" "image": "https://placehold.co/250x250?text=1"
}, },
{ {
"id": 403980494034923100, "id": 403980494034923100,
"name": "Cola", "name": "Cola",
"price": "1", "price": "1",
"deposit": "0.25",
"image": "https://placehold.co/250x250?text=2" "image": "https://placehold.co/250x250?text=2"
}, },
{ {
"id": 174111295271372860, "id": 174111295271372860,
"name": "Fanta", "name": "Fanta",
"price": "1", "price": "1",
"deposit": "0.25",
"image": "https://placehold.co/250x250?text=3" "image": "https://placehold.co/250x250?text=3"
}, },
{ {
"id": 13086165444583086, "id": 13086165444583086,
"name": "Bier", "name": "Bier",
"price": "1.5", "price": "1.5",
"deposit": "0.07",
"image": "https://placehold.co/250x250?text=4" "image": "https://placehold.co/250x250?text=4"
}, },
{ {
"id": 227920082494117630, "id": 227920082494117630,
"name": "Helles Bier", "name": "Helles Bier",
"price": "1.2", "price": "1.2",
"deposit": null,
"image": "https://placehold.co/250x250?text=5" "image": "https://placehold.co/250x250?text=5"
}, },
{ {
"id": 27617349687000040, "id": 27617349687000040,
"name": "Apfelsaft", "name": "Apfelsaft",
"price": "1.2", "price": "1.2",
"deposit": null,
"image": "https://placehold.co/250x250?text=6" "image": "https://placehold.co/250x250?text=6"
}, },
{ {
"id": 191783545996520450, "id": 191783545996520450,
"name": "Eistee", "name": "Eistee",
"price": "1.5", "price": "1.5",
"deposit": null,
"image": "https://placehold.co/250x250?text=7" "image": "https://placehold.co/250x250?text=7"
}, },
{ {
"id": 203321763516746460, "id": 203321763516746460,
"name": "Cider", "name": "Cider",
"price": "1.8", "price": "1.8",
"deposit": null,
"image": "https://placehold.co/250x250?text=8" "image": "https://placehold.co/250x250?text=8"
}, },
{ {
"id": 615216202376277900, "id": 615216202376277900,
"name": "Zitronenlimonade", "name": "Zitronenlimonade",
"price": "1", "price": "1",
"deposit": null,
"image": "https://placehold.co/250x250?text=9" "image": "https://placehold.co/250x250?text=9"
}, },
{ {
"id": 363150314621734500, "id": 363150314621734500,
"name": "Wodka Lemon", "name": "Wodka Lemon",
"price": "3", "price": "3",
"deposit": null,
"image": "https://placehold.co/250x250?text=10" "image": "https://placehold.co/250x250?text=10"
} }
] ]

View file

@ -77,15 +77,34 @@ class Element {
return document.querySelector('.navbar-brand'); return document.querySelector('.navbar-brand');
} }
/**
* Get the buttons for importing test data
* @returns {Element}
*/
static getButtonsImportTestdata() { static getButtonsImportTestdata() {
return document.querySelectorAll('[data-action=import-testdata]'); return document.querySelectorAll('[data-action=import-testdata]');
} }
/**
* Get the buttons for showing the test data
* @returns {Element}
*/
static getButtonShowTestdata() { static getButtonShowTestdata() {
return document.querySelectorAll('[data-action=import-show-testdata]'); return document.querySelectorAll('[data-action=import-show-testdata]');
} }
/**
* Get the button for clearing the test data
* @returns {Element}
*/
static getButtonClearTestdata() { static getButtonClearTestdata() {
return document.querySelectorAll('[data-action=import-clear]'); return document.querySelectorAll('[data-action=import-clear]');
} }
/**
* Get the button for accessing the GitHub repository
* @returns {Element}
*/
static getGitHubReferenceLink() { static getGitHubReferenceLink() {
return document.querySelector('[data-github-ref]'); return document.querySelector('[data-github-ref]');
} }
@ -122,6 +141,15 @@ class TemplateElement {
return this.removeTemplate(element); return this.removeTemplate(element);
} }
/**
* Get the deposit element template
* @returns {HTMLElement}
*/
static getDepositTemplate() {
const element = document.querySelector('[data-template=product-deposit]').cloneNode(true);
return this.removeTemplate(element);
}
/** /**
* Remove the template attribute from the element * Remove the template attribute from the element
* @param element {HTMLElement} * @param element {HTMLElement}
@ -141,13 +169,15 @@ class Product {
* Product constructor * Product constructor
* @param name {string} Name of the product * @param name {string} Name of the product
* @param price {number} Price of the product * @param price {number} Price of the product
* @param deposit {number} Deposit of the product
* @param image {string} Image of the product (source url or base64) * @param image {string} Image of the product (source url or base64)
*/ */
constructor(name, price, image) { constructor(name, price, deposit, image) {
this.id = Product.generateId(); this.id = Product.generateId();
this.name = name; this.name = name;
this.price = price; this.price = price;
this.image = image; this.image = image;
this.deposit = deposit;
} }
/** /**
@ -324,7 +354,7 @@ class ProductManager {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async convertAndSaveProductImages(json) { async convertAndSaveProductImages(json) {
const products = json.map(e => new Product(e.name, e.price, e.image)); const products = json.map(e => new Product(e.name, e.price, e.deposit, e.image));
for (const product of products) { for (const product of products) {
if (!product.image.toString().startsWith('data:image/')) { if (!product.image.toString().startsWith('data:image/')) {
product.image = await Base64Image.imageResize(await Base64Image.fromImageUrl(product.image)) product.image = await Base64Image.imageResize(await Base64Image.fromImageUrl(product.image))
@ -349,7 +379,7 @@ class ProductManager {
try { try {
parse = JSON.parse(json); parse = JSON.parse(json);
} catch (e) {} } catch (e) {}
this.products = parse.map(e => new Product(e.name, e.price, e.image)); this.products = parse.map(e => new Product(e.name, e.price, e.deposit, e.image));
this.renderProductList(); this.renderProductList();
} }
@ -362,6 +392,7 @@ class ProductManager {
const name = document.querySelector('#product-name').value; const name = document.querySelector('#product-name').value;
const price = document.querySelector('#product-price').value; const price = document.querySelector('#product-price').value;
const deposit = document.querySelector('#product-deposit').value;
const image = document.querySelector('#product-image').files[0]; const image = document.querySelector('#product-image').files[0];
// Validate price // Validate price
@ -374,12 +405,12 @@ class ProductManager {
if (image) { if (image) {
const imageSrcWithBase64 = Base64Image.fromFile(image); const imageSrcWithBase64 = Base64Image.fromFile(image);
imageSrcWithBase64.then(async imageSrcWithBase64 => { imageSrcWithBase64.then(async imageSrcWithBase64 => {
productManager.addProduct(new Product(name, price, await Base64Image.imageResize(imageSrcWithBase64))); productManager.addProduct(new Product(name, price, deposit, await Base64Image.imageResize(imageSrcWithBase64)));
ProductManager.resetProductSettingsForm(); ProductManager.resetProductSettingsForm();
}); });
} else { } else {
let image1 = await Base64Image.imageResize(await Base64Image.fromImageUrl(`https://placehold.co/250x250?text=${name}`)); let imageDemoBase64 = await Base64Image.imageResize(await Base64Image.fromImageUrl(`https://placehold.co/250x250?text=${name}`));
productManager.addProduct(new Product(name, price, image1)); productManager.addProduct(new Product(name, price, deposit, imageDemoBase64));
ProductManager.resetProductSettingsForm(); ProductManager.resetProductSettingsForm();
} }
}); });
@ -450,7 +481,7 @@ class CartManager {
registerCartResetEvent() { registerCartResetEvent() {
Element.getCartButton().addEventListener('click', () => { Element.getCartButton().addEventListener('click', () => {
CartHistoryManager.addToTotal(this.cartLines.reduce((acc, cartLine) => { CartHistoryManager.addToTotal(this.cartLines.reduce((acc, cartLine) => {
return acc + (cartLine.product.price * cartLine.quantity); return acc + (cartLine.product.price * cartLine.quantity) + ((cartLine.product.deposit??0) * cartLine.quantity);
}, 0)); }, 0));
this.cartLines = []; this.cartLines = [];
this.renderCart(); this.renderCart();
@ -491,7 +522,7 @@ class CartManager {
*/ */
renderCart() { renderCart() {
// Clear the cart element before rendering except the template // Clear the cart element before rendering except the template
Element.getCartElement().querySelectorAll('[data-id]').forEach(e => { Element.getCartElement().querySelectorAll('[data-id], .product-deposit').forEach(e => {
if (e.getAttribute('data-template') === null) { if (e.getAttribute('data-template') === null) {
e.remove(); e.remove();
} }
@ -509,6 +540,16 @@ class CartManager {
const cartLineElement = this.getCartLineElement(cartLine); const cartLineElement = this.getCartLineElement(cartLine);
Element.getCartElement().appendChild(cartLineElement); Element.getCartElement().appendChild(cartLineElement);
}); });
let depositTotal = 0;
this.cartLines.forEach(cartLine => {
depositTotal += (cartLine.product.deposit??0) * cartLine.quantity;
});
if(depositTotal !== 0) {
let depositElement = TemplateElement.getDepositTemplate();
depositElement.querySelector('[data-attr=deposit]').textContent = CartManager.getNumberFormatter().format(depositTotal);
Element.getCartElement().appendChild(depositElement);
}
} }
/** /**
@ -533,7 +574,7 @@ class CartManager {
*/ */
calculateCartValue() { calculateCartValue() {
let cartValue = this.cartLines.reduce((acc, cartLine) => { let cartValue = this.cartLines.reduce((acc, cartLine) => {
return acc + (cartLine.product.price * cartLine.quantity); return acc + (cartLine.product.price * cartLine.quantity) + ((cartLine.product.deposit??0) * cartLine.quantity);
}, 0); }, 0);
Element.getCartButton().querySelector('[data-total-value]').textContent = CartManager.getNumberFormatter().format(cartValue); Element.getCartButton().querySelector('[data-total-value]').textContent = CartManager.getNumberFormatter().format(cartValue);
} }

View file

@ -6,9 +6,9 @@ const FILES_TO_CACHE = [
`${BASE_PATH}/`, `${BASE_PATH}/`,
`${BASE_PATH}/index.html`, `${BASE_PATH}/index.html`,
`${BASE_PATH}/assets/manifest.json`, `${BASE_PATH}/assets/manifest.json`,
`${BASE_PATH}/assets/script/all.js`,
`${BASE_PATH}/assets/script/service-worker.js`, `${BASE_PATH}/assets/script/service-worker.js`,
`${BASE_PATH}/assets/style/stylesheet.css`, `${BASE_PATH}/assets/style/stylesheet.css`,
`${BASE_PATH}/assets/script/all.js`,
`${BASE_PATH}/node_modules/bootstrap/dist/css/bootstrap.min.css`, `${BASE_PATH}/node_modules/bootstrap/dist/css/bootstrap.min.css`,
`${BASE_PATH}/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js`, `${BASE_PATH}/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js`,
`${BASE_PATH}/node_modules/bootstrap-icons/font/bootstrap-icons.min.css`, `${BASE_PATH}/node_modules/bootstrap-icons/font/bootstrap-icons.min.css`,

View file

@ -48,6 +48,9 @@
font-weight: bold; font-weight: bold;
} }
} }
.product-deposit {
cursor: default;
}
} }
/** /**
* Product list contains all available products, * Product list contains all available products,

View file

@ -45,7 +45,12 @@
</span> </span>
<i class="bi bi-dash-circle"></i> <i class="bi bi-dash-circle"></i>
</li> </li>
<ul class="list-group cart-items"></ul> <li class="list-group-item product-deposit" data-template="product-deposit">
Zzgl. <span data-attr="deposit" class="currency-value">0,00</span> Pfand
</li>
<ul class="list-group cart-items">
</ul>
<div class="alert alert-info cart-empty">Keine Produkte ausgewählt</div> <div class="alert alert-info cart-empty">Keine Produkte ausgewählt</div>
<hr> <hr>
<button class="btn btn-secondary btn-lg w-100 cart-value mb-3"> <button class="btn btn-secondary btn-lg w-100 cart-value mb-3">
@ -85,6 +90,13 @@
<input type="number" class="form-control" step="0.01" value="" id="product-price" required> <input type="number" class="form-control" step="0.01" value="" id="product-price" required>
</div> </div>
</div> </div>
<div class="mb-2">
<label for="product-deposit" class="form-label">Pfand</label>
<div class="input-group">
<span class="input-group-text" id="basic-addon2"></span>
<input type="number" class="form-control" step="0.01" value="" id="product-deposit" required>
</div>
</div>
<div class="mb-2"> <div class="mb-2">
<label for="product-image" class="form-label">Bild auswählen</label> <label for="product-image" class="form-label">Bild auswählen</label>
<input type="file" class="form-control" placeholder="Tolles Produkt" id="product-image"> <input type="file" class="form-control" placeholder="Tolles Produkt" id="product-image">

View file

@ -1,7 +1,9 @@
{ {
"name": "durst-rechner", "name": "durst-rechner",
"version": "1.0.0", "version": "1.0.0",
"scripts": {}, "scripts": {
"dev": "npx http-server -o ."
},
"keywords": [ "keywords": [
"calculator", "calculator",
"feuerwehr", "feuerwehr",