added prototype
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Modal } from 'bootstrap'; // ohne Pfadangabe (Modul auslesen aus node_modules) funktioniert nur mit JS-Bundler (esbuild, rollup & co.)
|
||||
import Toast from './Toast';
|
||||
import { querySelectorTyped } from '../utils/helpers';
|
||||
import { querySelectorTyped, parents } from '../utils/helpers';
|
||||
|
||||
// import * as bootstrap from 'bootstrap';
|
||||
// import 'bootstrap';
|
||||
@@ -15,21 +15,28 @@ interface DOM {
|
||||
inputName: HTMLInputElement;
|
||||
inputPrice: HTMLInputElement;
|
||||
btnAdd: HTMLButtonElement;
|
||||
templateRow: HTMLTableRowElement;
|
||||
templateRow: HTMLTemplateElement;
|
||||
btnSave: HTMLButtonElement;
|
||||
btnReset: HTMLButtonElement;
|
||||
modalEdit: HTMLElement;
|
||||
modalEdit: HTMLDivElement;
|
||||
formEdit: HTMLFormElement;
|
||||
inputEditName: HTMLInputElement;
|
||||
inputEditPrice: HTMLInputElement;
|
||||
inputEditId: HTMLInputElement;
|
||||
inputEditPosition: HTMLInputElement;
|
||||
btnUpdate: HTMLButtonElement;
|
||||
modalDelete: HTMLElement;
|
||||
productName: HTMLElement;
|
||||
modalDelete: HTMLDivElement;
|
||||
productName: HTMLDivElement;
|
||||
btnConfirmDelete: HTMLButtonElement;
|
||||
}
|
||||
|
||||
interface Product {
|
||||
_id?: string;
|
||||
name: string;
|
||||
price: number;
|
||||
position?: number;
|
||||
}
|
||||
|
||||
// Factory Function
|
||||
const ProductManager = (el?: HTMLElement) => {
|
||||
// === DOM & VARS =======
|
||||
@@ -47,14 +54,14 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
inputName: querySelectorTyped<HTMLInputElement>('.input-product-name', module),
|
||||
inputPrice: querySelectorTyped<HTMLInputElement>('.input-product-price', module),
|
||||
btnAdd: querySelectorTyped<HTMLButtonElement>('.button-product-add', module),
|
||||
templateRow: querySelectorTyped<HTMLTableRowElement>('.template-row', module),
|
||||
templateRow: querySelectorTyped<HTMLTemplateElement>('.template-row', module),
|
||||
|
||||
// Nav Actions
|
||||
btnSave: querySelectorTyped<HTMLButtonElement>('.button-products-save', module),
|
||||
btnReset: querySelectorTyped<HTMLButtonElement>('.button-products-reset', module),
|
||||
|
||||
// Modal Edit
|
||||
modalEdit: querySelectorTyped<HTMLElement>('.modal-edit', module),
|
||||
modalEdit: querySelectorTyped<HTMLDivElement>('.modal-edit', module),
|
||||
formEdit: querySelectorTyped<HTMLFormElement>('.form-product-edit', module),
|
||||
inputEditName: querySelectorTyped<HTMLInputElement>('.input-edit-name', module),
|
||||
inputEditPrice: querySelectorTyped<HTMLInputElement>('.input-edit-price', module),
|
||||
@@ -63,8 +70,8 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
btnUpdate: querySelectorTyped<HTMLButtonElement>('.button-product-update', module),
|
||||
|
||||
// Modal Delete
|
||||
modalDelete: querySelectorTyped<HTMLElement>('.modal-delete', module),
|
||||
productName: querySelectorTyped<HTMLElement>('strong.product-name', module),
|
||||
modalDelete: querySelectorTyped<HTMLDivElement>('.modal-delete', module),
|
||||
productName: querySelectorTyped<HTMLDivElement>('strong.product-name', module),
|
||||
btnConfirmDelete: querySelectorTyped<HTMLButtonElement>('.button-confirm-delete', module),
|
||||
};
|
||||
|
||||
@@ -187,9 +194,9 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
};
|
||||
|
||||
const onClickEdit = (e: PointerEvent) => {
|
||||
const btnEl = e.currentTarget;
|
||||
const btnEl = e.currentTarget as HTMLButtonElement;
|
||||
const currentRow = parents(btnEl, 'tr')[0];
|
||||
const id = currentRow.dataset.id;
|
||||
const id: string = String(currentRow.dataset.id);
|
||||
|
||||
// async fetch
|
||||
getProduct(id).then((data) => {
|
||||
@@ -199,11 +206,11 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
};
|
||||
|
||||
const onClickRemove = (e: PointerEvent) => {
|
||||
const btnEl = e.currentTarget;
|
||||
const btnEl = e.currentTarget as HTMLButtonElement;
|
||||
const currentRow = parents(btnEl, 'tr')[0];
|
||||
const id = currentRow.dataset.id;
|
||||
|
||||
const productName = currentRow.querySelector('.td-name').textContent.trim();
|
||||
const productName = querySelectorTyped<HTMLTableCellElement>('.td-name', currentRow).textContent.trim();
|
||||
|
||||
DOM.btnConfirmDelete.dataset.id = id;
|
||||
DOM.productName.textContent = productName;
|
||||
@@ -223,7 +230,7 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
|
||||
const onClickConfirmDelete = (e: PointerEvent) => {
|
||||
const btnEl = e.currentTarget as HTMLButtonElement;
|
||||
const id = btnEl.dataset.id;
|
||||
const id: string = String(btnEl.dataset.id);
|
||||
|
||||
// async fetch
|
||||
deleteProduct(id).then((data) => {
|
||||
@@ -239,7 +246,7 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
};
|
||||
|
||||
const onClickMoveUp = (e: PointerEvent) => {
|
||||
const btnEl = e.currentTarget;
|
||||
const btnEl = e.currentTarget as HTMLButtonElement;
|
||||
// const currentRowEl = btnEl.parentNode.parentNode; // aktuelle Zeile (tr) mit dem Button
|
||||
const currentRowEl = parents(btnEl, 'tr')[0];
|
||||
|
||||
@@ -247,14 +254,14 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
// DOM.tBody.insertBefore(currentRowEl, currentRowEl.previousElementSibling);
|
||||
|
||||
// targetElement.before(referenceElement)
|
||||
currentRowEl.previousElementSibling.before(currentRowEl);
|
||||
currentRowEl.previousElementSibling!.before(currentRowEl);
|
||||
|
||||
console.log('move up');
|
||||
disableNonFunctionalButtons();
|
||||
};
|
||||
|
||||
const onClickMoveDown = (e: PointerEvent) => {
|
||||
const btnEl = e.currentTarget;
|
||||
const btnEl = e.currentTarget as HTMLButtonElement;
|
||||
// const currentRowEl = btnEl.parentNode.parentNode; // aktuelle Zeile (tr) mit dem Button
|
||||
const currentRowEl = parents(btnEl, 'tr')[0];
|
||||
|
||||
@@ -262,7 +269,7 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
// DOM.tBody.insertBefore(currentRowEl, currentRowEl.nextElementSibling.nextElementSibling);
|
||||
|
||||
// targetElement.after(referenceElement)
|
||||
currentRowEl.nextElementSibling.after(currentRowEl);
|
||||
currentRowEl.nextElementSibling!.after(currentRowEl);
|
||||
|
||||
console.log('move down');
|
||||
disableNonFunctionalButtons();
|
||||
@@ -270,13 +277,13 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
|
||||
// Drag 'n Drop EventHandler
|
||||
const onMouseDownDrag = (e: MouseEvent) => {
|
||||
const btnEl = e.currentTarget;
|
||||
const btnEl = e.currentTarget as HTMLButtonElement;
|
||||
const currentRowEl = parents(btnEl, 'tr')[0];
|
||||
currentRowEl.draggable = true;
|
||||
};
|
||||
|
||||
const onMouseUpDrag = (e: MouseEvent) => {
|
||||
const btnEl = e.currentTarget;
|
||||
const btnEl = e.currentTarget as HTMLButtonElement;
|
||||
const currentRowEl = parents(btnEl, 'tr')[0];
|
||||
currentRowEl.draggable = false;
|
||||
};
|
||||
@@ -369,8 +376,12 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
const data = await response.json(); // HTTP-Response
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
Toast(error, 'error').show();
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
Toast(error.message, 'error').show();
|
||||
} else {
|
||||
Toast('An unknown error occurred.', 'error').show();
|
||||
}
|
||||
|
||||
console.error(error);
|
||||
return error;
|
||||
@@ -380,7 +391,7 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
// HTTP - Methode - POST
|
||||
// (C)RUD - create
|
||||
// BRE(A)D - add - neues Produkt hinzufügen
|
||||
const createProduct = async (product) => {
|
||||
const createProduct = async (product: Product) => {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/api/products`, {
|
||||
method: 'POST',
|
||||
@@ -418,8 +429,12 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
const data = await response.json(); // HTTP-Response
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
} else {
|
||||
console.error('An unknown error occurred.');
|
||||
}
|
||||
return error;
|
||||
}
|
||||
};
|
||||
@@ -465,8 +480,12 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
const data = await response.json(); // HTTP-Response
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
} else {
|
||||
console.error('An unknown error occurred.');
|
||||
}
|
||||
return error;
|
||||
}
|
||||
};
|
||||
@@ -497,8 +516,8 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
// === FUNCTIONS ========
|
||||
|
||||
const initProducts = () => {
|
||||
getProducts().then((products) => {
|
||||
products.forEach((product) => {
|
||||
getProducts().then((products: Product[]) => {
|
||||
products.forEach((product: Product) => {
|
||||
addProduct(product);
|
||||
});
|
||||
Toast('Init products').show();
|
||||
@@ -514,31 +533,31 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
|
||||
const loadProducts = () => {
|
||||
DOM.tBody.innerHTML = '';
|
||||
getProducts().then((data) => {
|
||||
data.forEach((product) => {
|
||||
getProducts().then((data: Product[]) => {
|
||||
data.forEach((product: Product) => {
|
||||
addProduct(product);
|
||||
});
|
||||
disableNonFunctionalButtons();
|
||||
});
|
||||
};
|
||||
|
||||
const addProduct = (product) => {
|
||||
const addProduct = (product: Product) => {
|
||||
const { name, price, _id: id, position } = product;
|
||||
|
||||
// https://developer.mozilla.org/de/docs/Web/API/HTMLTemplateElement/content
|
||||
const trEl = DOM.templateRow.content.firstElementChild.cloneNode(true);
|
||||
const trEl = DOM.templateRow.content.firstElementChild?.cloneNode(true) as HTMLTableRowElement;
|
||||
|
||||
const tdNameEl = trEl.querySelector('.td-name');
|
||||
const tdPriceEl = trEl.querySelector('.td-price');
|
||||
const btnRemoveEl = trEl.querySelector('.button-product-remove');
|
||||
const btnMoveUpEl = trEl.querySelector('.button-product-move-up');
|
||||
const btnMoveDownEl = trEl.querySelector('.button-product-move-down');
|
||||
const tdNameEl = querySelectorTyped<HTMLTableCellElement>('.td-name', trEl);
|
||||
const tdPriceEl = querySelectorTyped<HTMLTableCellElement>('.td-price', trEl);
|
||||
const btnRemoveEl = querySelectorTyped<HTMLButtonElement>('.button-product-remove', trEl);
|
||||
const btnMoveUpEl = querySelectorTyped<HTMLButtonElement>('.button-product-move-up', trEl);
|
||||
const btnMoveDownEl = querySelectorTyped<HTMLButtonElement>('.button-product-move-down', trEl);
|
||||
|
||||
const btnDragEl = trEl.querySelector('.button-drag');
|
||||
const btnEditEl = trEl.querySelector('.button-product-edit');
|
||||
const btnDragEl = querySelectorTyped<HTMLButtonElement>('.button-drag', trEl);
|
||||
const btnEditEl = querySelectorTyped<HTMLButtonElement>('.button-product-edit', trEl);
|
||||
|
||||
tdNameEl.textContent = name;
|
||||
tdPriceEl.textContent = price;
|
||||
tdNameEl.textContent = String(name);
|
||||
tdPriceEl.textContent = String(price);
|
||||
|
||||
btnEditEl.addEventListener('click', onClickEdit);
|
||||
btnRemoveEl.addEventListener('click', onClickRemove);
|
||||
@@ -554,19 +573,19 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
trEl.addEventListener('drop', onDrop);
|
||||
|
||||
trEl.dataset.id = id; // <tr data-id="..." >...</tr>
|
||||
trEl.dataset.position = position; // <tr data-position="..." >...</tr>
|
||||
trEl.dataset.position = String(position); // <tr data-position="..." >...</tr>
|
||||
|
||||
DOM.tBody.appendChild(trEl); // Im DOM hinzufügen
|
||||
};
|
||||
|
||||
const showModalEdit = (product) => {
|
||||
const showModalEdit = (product: Product) => {
|
||||
const { name = '', price = 0, _id: id, position } = product;
|
||||
|
||||
DOM.inputEditName.value = name;
|
||||
DOM.inputEditPrice.value = price;
|
||||
DOM.inputEditPrice.value = String(price);
|
||||
|
||||
DOM.inputEditId.value = id;
|
||||
DOM.inputEditPosition.value = position;
|
||||
DOM.inputEditId.value = String(id);
|
||||
DOM.inputEditPosition.value = String(position);
|
||||
};
|
||||
|
||||
const resetFields = () => {
|
||||
@@ -579,23 +598,14 @@ const ProductManager = (el?: HTMLElement) => {
|
||||
|
||||
const disableNonFunctionalButtons = () => {
|
||||
Array.from(DOM.tBody.querySelectorAll('tr')).forEach((tr) => {
|
||||
const btnMoveUp = tr.querySelector('.button-product-move-up');
|
||||
const btnMoveDown = tr.querySelector('.button-product-move-down');
|
||||
const btnMoveUp = querySelectorTyped<HTMLButtonElement>('.button-product-move-up', tr);
|
||||
const btnMoveDown = querySelectorTyped<HTMLButtonElement>('.button-product-move-down', tr);
|
||||
|
||||
btnMoveUp.disabled = !tr.previousElementSibling;
|
||||
btnMoveDown.disabled = !tr.nextElementSibling;
|
||||
});
|
||||
};
|
||||
|
||||
// Helferfunktion aus jQuery (youmightnotjquery)
|
||||
const parents = (el, selector) => {
|
||||
const parents = [];
|
||||
while ((el = el.parentNode) && el !== document) {
|
||||
if (!selector || el.matches(selector)) parents.push(el);
|
||||
}
|
||||
return parents;
|
||||
};
|
||||
|
||||
init();
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user