Compare commits

..

No commits in common. "9eba8c47f495ca3570e8ba79a2685061cca1edac" and "ab65fa7a73ba2ad430a9bafdb6a160189e0eadfb" have entirely different histories.

3 changed files with 12 additions and 169 deletions

View file

@ -21,30 +21,6 @@ class Element {
return document.querySelector('#input-export-import');
}
/**
* Get the file export button element
* @returns {Element}
*/
static getFileExportButton() {
return document.querySelector('#export-file');
}
/**
* Get the file import button element
* @returns {Element}
*/
static getFileImportButton() {
return document.querySelector('#import-file-button');
}
/**
* Get the file import input element
* @returns {Element}
*/
static getFileImportFileInput() {
return document.querySelector('#import-file');
}
/**
* Get the product list element
* @returns {HTMLElement}
@ -103,7 +79,7 @@ class Element {
/**
* Get the buttons for importing test data
* @returns {Element[]}
* @returns {Element}
*/
static getButtonsImportTestdata() {
return document.querySelectorAll('[data-action=import-testdata]');
@ -235,46 +211,12 @@ class Product {
getSettingsProductElement() {
const productElement = TemplateElement.getSettingsProductTemplate();
productElement.setAttribute('data-id', this.id);
productElement.querySelector('[data-attr=name]').textContent = this.name;
productElement.querySelector('[data-action=delete]').addEventListener('click', () => {
productElement.textContent = this.name;
productElement.addEventListener('click', () => {
productManager.removeProduct(this);
productManager.setExportFieldJsonValue();
productElement.remove();
})
productElement.querySelector('[data-action=edit]').addEventListener('click', () => {
const nameInputField = document.querySelector('#product-name');
const priceInputField = document.querySelector('#product-price');
const depositInputField = document.querySelector('#product-deposit');
const imageInputField = document.querySelector('#product-image');
const editProductId = document.querySelector("[name=edit-product-id]");
nameInputField.value = this.name;
priceInputField.value = this.price;
depositInputField.value = this.deposit;
editProductId.value = this.id;
// Set the image input field, create a new file object and set it to the input field by base64
const base64Image = this.image.split(',')[1];
const byteCharacters = atob(base64Image);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: 'image/png' });
const file = new File([blob], 'image.png', { type: 'image/png' });
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
imageInputField.files = dataTransfer.files;
imageInputField.dispatchEvent(new Event('change'));
// Remove the product element from the settings list if the product has been saved
Element.getCreateNewProductButton().addEventListener('click', (e) => {
if(document.querySelector("[name=edit-product-id]").value === this.id.toString()) {
productManager.removeProduct(this);
productElement.remove();
}
});
})
return productElement;
}
}
@ -332,8 +274,8 @@ class Base64Image {
img.crossOrigin = 'Anonymous';
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = 150;
canvas.height = 150;
canvas.width = 250;
canvas.height = 250;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
const dataUrl = canvas.toDataURL();
@ -404,34 +346,6 @@ class ProductManager {
console.error(e);
}
});
Element.getFileExportButton().addEventListener('click', () => {
const blob = new Blob([Element.getImportExportTextarea().value], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'products.json';
document.body.appendChild(a);
a.click();
setTimeout(() => {
URL.revokeObjectURL(url);
a.remove();
}, 0);
});
Element.getFileImportButton().addEventListener('click', () => {
let inputJsonFile = Element.getFileImportFileInput();
let fileContent = inputJsonFile.files[0];
if (fileContent) {
const reader = new FileReader();
reader.onload = async (e) => {
const json = JSON.parse(e.target.result);
await productManager.convertAndSaveProductImages(json);
location.reload();
};
reader.readAsText(fileContent);
}
})
}
/**
@ -506,7 +420,7 @@ class ProductManager {
* Reset the product settings form by clearing all input fields.
*/
static resetProductSettingsForm() {
document.querySelectorAll('form input:not([type=submit], [type=reset])').forEach(inputField => inputField.value = '');
document.querySelectorAll('form input:not([type=submit])').forEach(inputField => inputField.value = '');
}
/**
@ -768,26 +682,14 @@ class ThemeManager {
* Define the CartHistoryManager class to manage the cart history.
*/
class CartHistoryManager {
/**
* Get the cart history from local storage.
* @returns {string|number}
*/
static getTotal() {
return localStorage.getItem('cart-total-value') || 0;
}
/**
* Set the cart history value in local storage.
* @param value
*/
static setTotal(value) {
localStorage.setItem('cart-total-value', value);
}
/**
* Add to the cart history value in local storage.
* @param value
*/
static addToTotal(value) {
const total = parseFloat(CartHistoryManager.getTotal()) + value;
CartHistoryManager.setTotal(total);

View file

@ -20,7 +20,7 @@
* Make product boxes, cart items and settings product list clickable
* This is generally used for product list and cart items
*/
.product-box, .cart-items, .navbar-brand {
.product-box, .cart-items, .settings-product-list, .navbar-brand {
cursor: pointer;
}
/**
@ -52,26 +52,13 @@
cursor: default;
}
}
/**
* Make the cart sticky on screens wider than 768px
*/
@media screen and (min-width: 768px) {
.cart {
position: sticky;
top: 1rem;
max-height: calc(100vh - 2rem);
overflow-y: auto;
}
}
/**
* Product list contains all available products,
* which are displayed in a grid layout. The grid scaling is done by bootstrap.
*/
.product-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 4fr));
grid-template-columns: repeat(auto-fill, minmax(200px, 4fr));
gap: .4em;
padding-bottom: 1em;
@ -93,10 +80,7 @@
-khtml-user-drag: none;
user-drag: none;
}
/* Make the product box responsive */
.card {
height: 100%;
}
/* Break the words on the card body */
.card-body span {
word-wrap: break-word;
@ -105,10 +89,6 @@
.product-box small[data-attr=price] {
font-weight: 400;
}
/* Make the product box clickable */
&:active{
transform:scale(0.96);
}
}
}
/* When using dark mode, use info color for price */

View file

@ -101,9 +101,7 @@
<label for="product-image" class="form-label">Bild auswählen</label>
<input type="file" class="form-control" placeholder="Tolles Produkt" id="product-image">
</div>
<input type="hidden" name="edit-product-id">
<input type="submit" class="btn btn-success" value="Speichern" id="create-product">
<input type="reset" class="btn btn-secondary" value="Zurücksetzen" onclick="document.querySelector('[name=edit-product-id]').value = ''">
</div>
</form>
@ -112,35 +110,6 @@
<div class="card-body">
<details class="mb-2">
<summary>Exportieren und Importieren</summary>
<div class="row">
<div class="col-12 mb-2 mt-2">
<p>
Hier kann die JSON-Datei exportiert werden. Dafür auf "Produkte herunterladen" klicken.
Die Datei wird dann im Download-Ordner gespeichert. Die Daten können nach eigenen
Belieben bearbeitet werden. Die Daten können auch wieder importiert werden.
</p>
<button class="btn btn-primary" id="export-file">
Produkte herunterladen
</button>
<hr>
</div>
<div class="col-12">
<p>
Hier kann die JSON-Datei importiert werden. Dafür die Datei
auswählen und auf "Hochladen" klicken. Die Daten werden dann in die Software
importiert.
</p>
<div class="input-group">
<input class="form-control" type="file" id="import-file">
<button class="btn btn-primary ms-2" data-action="import" id="import-file-button">
Hochladen
</button>
</div>
</div>
</div>
</details>
<details class="mb-2">
<summary>Testdaten einspielen</summary>
<p>
Die Daten können im JSON-Format exportiert und importiert werden.
Um Speicherplatz zu sparen, sollte das Bild im Format 1:1 vorliegen.
@ -179,18 +148,10 @@
</aside>
<div class="col-12 col-md-6 mt-2 mt-md-0">
<div class="card">
<h5 class="card-header">Produkte</h5>
<h5 class="card-header">Produkte entfernen</h5>
<div class="card-body">
<p class="mb-2">Hier können Produkte entfernt oder bearbeitet werden.</p>
<li class="list-group-item" data-template="settings-product">
<span class="d-flex justify-content-between">
<span data-attr="name"></span>
<span>
<button class="mx-auto btn btn-secondary btn-sm" data-action="edit"><i class="bi bi-pen"></i></button>
<button class="mx-auto btn btn-warning btn-sm" data-action="delete"><i class="bi bi-trash"></i></button>
</span>
</span>
</li>
<p class="mb-2">Hier können Produkte entfernt werden, die nicht mehr benötigt werden.</p>
<li class="list-group-item" data-template="settings-product">Test</li>
<ul class="list-group settings-product-list">
</ul>
</div>