This commit is contained in:
@@ -30,7 +30,6 @@ import ProductManager from './modules/ProductManager'; // Dateiendung kann wegge
|
||||
break;
|
||||
case '/projects/product-manager.html':
|
||||
console.log('PRODUCT_MANAGER');
|
||||
|
||||
const pm = ProductManager();
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { Modal } from 'bootstrap'; // ohne Pfadangabe (Modul auslesen aus node_modules) funktioniert nur mit JS-Bundler (esbuild, rollup & co.)
|
||||
import Toast from './Toast';
|
||||
|
||||
// import * as bootstrap from 'bootstrap';
|
||||
// import 'bootstrap';
|
||||
|
||||
// import 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js'; // würde auch ohne Bundler funktionieren
|
||||
// import '../../../node_modules/bootstrap/dist/js/bootstrap.esm.js'; // würde auch ohne Bundler funktionieren
|
||||
@@ -22,21 +26,31 @@ const ProductManager = (el = null) => {
|
||||
btnAdd: module.querySelector('.button-product-add'),
|
||||
templateRow: module.querySelector('.template-row'),
|
||||
|
||||
// Modal
|
||||
// Modal Edit
|
||||
modalEdit: module.querySelector('.modal-edit'),
|
||||
formEdit: module.querySelector('.form-product-edit'),
|
||||
inputEditName: module.querySelector('.input-edit-name'),
|
||||
inputEditPrice: module.querySelector('.input-edit-price'),
|
||||
inputEditId: module.querySelector('.input-edit-id'),
|
||||
inputEditPosition: module.querySelector('.input-edit-position'),
|
||||
btnUpdate: module.querySelector('.button-product-update'),
|
||||
|
||||
// Modal Delete
|
||||
modalDelete: module.querySelector('.modal-delete'),
|
||||
productName: module.querySelector('strong.product-name'),
|
||||
btnConfirmDelete: module.querySelector('.button-confirm-delete'),
|
||||
};
|
||||
|
||||
console.log(DOM);
|
||||
const bsModalEdit = new Modal(DOM.modalEdit, {
|
||||
backdrop: 'static', // true
|
||||
keyboard: true,
|
||||
});
|
||||
|
||||
console.log(DOM);
|
||||
const bsModalDelete = new Modal(DOM.modalDelete, {
|
||||
backdrop: 'static', // true
|
||||
keyboard: true,
|
||||
});
|
||||
|
||||
// === INIT =============
|
||||
const init = () => {
|
||||
@@ -46,13 +60,27 @@ const ProductManager = (el = null) => {
|
||||
|
||||
// Event-Lauscher zu beginn der Anwendung
|
||||
DOM.btnAdd.addEventListener('click', onClickAdd);
|
||||
DOM.btnAdd.disabled = true;
|
||||
DOM.btnConfirmDelete.addEventListener('click', onClickConfirmDelete);
|
||||
DOM.formEdit.addEventListener('submit', onSubmitEdit);
|
||||
window.addEventListener('keyup', onKeyUp);
|
||||
};
|
||||
|
||||
// === EVENTHANDLER =====
|
||||
const onKeyUp = (e) => {
|
||||
// if (DOM.inputName.value === '' && DOM.inputPrice.value === '') {
|
||||
// DOM.btnAdd.disabled = true;
|
||||
// } else {
|
||||
// DOM.btnAdd.disabled = false;
|
||||
// }
|
||||
DOM.btnAdd.disabled = (DOM.inputName.value === '' || DOM.inputPrice.value === ''); // prettier-ignore
|
||||
};
|
||||
|
||||
const onSubmitEdit = (e) => {
|
||||
e.preventDefault(); // Standardverhalten unterbinden (Formular nicht an action versenden)
|
||||
|
||||
// console.log(Object.entries(new FormData(DOM.formEdit)));
|
||||
|
||||
const product = {
|
||||
name: DOM.inputEditName.value,
|
||||
price: Number(DOM.inputEditPrice.value),
|
||||
@@ -61,11 +89,17 @@ const ProductManager = (el = null) => {
|
||||
};
|
||||
|
||||
// update async process
|
||||
DOM.btnUpdate.querySelector('i').classList.add('fa-spin');
|
||||
updateProduct(product).then((data) => {
|
||||
console.log(data);
|
||||
if (data.success) {
|
||||
Toast(data.msg, 'success').show();
|
||||
DOM.btnUpdate.querySelector('i').classList.remove('fa-spin');
|
||||
loadProducts(); // All Produkte auslesen
|
||||
resetFields();
|
||||
bsModalEdit.hide();
|
||||
} else {
|
||||
Toast(data.msg, 'error').show();
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -82,7 +116,9 @@ const ProductManager = (el = null) => {
|
||||
console.log(data);
|
||||
if (data.success) {
|
||||
console.log('add');
|
||||
Toast(data.msg, 'success').show();
|
||||
loadProducts();
|
||||
resetFields();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -105,15 +141,42 @@ const ProductManager = (el = null) => {
|
||||
|
||||
const onClickRemove = (e) => {
|
||||
const btnEl = e.currentTarget;
|
||||
//const currentRowEl = btnEl.parentNode.parentNode; // parentNode -> td - parentNode -> tr
|
||||
// MDN https://developer.mozilla.org/de/docs/Web/API/Element/closest
|
||||
//const currentRowEl = btnEl.closest('tr');
|
||||
const currentRow = parents(btnEl, 'tr')[0];
|
||||
const id = currentRow.dataset.id;
|
||||
|
||||
const currentRowEl = parents(btnEl, 'tr')[0];
|
||||
const productName = currentRow.querySelector('.td-name').textContent.trim();
|
||||
|
||||
currentRowEl.remove(); // kein IE11 support
|
||||
// DOM.tBody.removeChild(currentRowEl);
|
||||
disableNonFunctionalButtons();
|
||||
DOM.btnConfirmDelete.dataset.id = id;
|
||||
DOM.productName.textContent = productName;
|
||||
|
||||
// async fetch
|
||||
// deleteProduct(id).then((data) => {
|
||||
// if (data.success) {
|
||||
// Toast(data.msg, 'success').show();
|
||||
// console.log('delete: success', data);
|
||||
// loadProducts();
|
||||
// } else {
|
||||
// Toast(data.msg, 'error').show();
|
||||
// console.error(data);
|
||||
// }
|
||||
// });
|
||||
};
|
||||
|
||||
const onClickConfirmDelete = (e) => {
|
||||
const btnEl = e.currentTarget;
|
||||
const id = btnEl.dataset.id;
|
||||
|
||||
// async fetch
|
||||
deleteProduct(id).then((data) => {
|
||||
if (data.success) {
|
||||
Toast(data.msg, 'success').show();
|
||||
bsModalDelete.hide();
|
||||
loadProducts();
|
||||
} else {
|
||||
Toast(data.msg, 'error').show();
|
||||
console.error(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onClickMoveUp = (e) => {
|
||||
@@ -220,6 +283,8 @@ const ProductManager = (el = null) => {
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
Toast(error, 'error').show();
|
||||
|
||||
console.error(error);
|
||||
return error;
|
||||
}
|
||||
@@ -285,6 +350,29 @@ const ProductManager = (el = null) => {
|
||||
}
|
||||
};
|
||||
|
||||
// HTTP - Methode - DELETE
|
||||
// CRU(D) - delete
|
||||
// BREA(D) - delete - Produkt löschen
|
||||
const deleteProduct = async (id) => {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/api/products/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}); // HTTP Request URL
|
||||
if (!response.ok) {
|
||||
throw new Error('Fetch went wrong.');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
// === FUNCTIONS ========
|
||||
|
||||
const initProducts = () => {
|
||||
@@ -292,6 +380,7 @@ const ProductManager = (el = null) => {
|
||||
products.forEach((product) => {
|
||||
addProduct(product);
|
||||
});
|
||||
Toast('Init products').show();
|
||||
disableNonFunctionalButtons();
|
||||
});
|
||||
|
||||
@@ -359,6 +448,14 @@ const ProductManager = (el = null) => {
|
||||
DOM.inputEditPosition.value = position;
|
||||
};
|
||||
|
||||
const resetFields = () => {
|
||||
DOM.btnAdd.disabled = true;
|
||||
DOM.inputPrice.value = '';
|
||||
DOM.inputName.value = '';
|
||||
|
||||
DOM.formEdit.reset();
|
||||
};
|
||||
|
||||
const disableNonFunctionalButtons = () => {
|
||||
Array.from(DOM.tBody.querySelectorAll('tr')).forEach((tr) => {
|
||||
const btnMoveUp = tr.querySelector('.button-product-move-up');
|
||||
@@ -383,6 +480,7 @@ const ProductManager = (el = null) => {
|
||||
return {
|
||||
init,
|
||||
initProducts,
|
||||
resetFields,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
59
webseite/frontend/dev/scripts/modules/Toast.js
Normal file
59
webseite/frontend/dev/scripts/modules/Toast.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import Toastify from 'toastify-js'; // das Modul wird von node_modules ausgelesen und gebundlet. Funktioniert nur mit JS-Bundler
|
||||
|
||||
const Toast = (text = '', variant = 'primary') => {
|
||||
const getColorByName = (variant = '') => {
|
||||
let color;
|
||||
switch (variant.trim().toLowerCase()) {
|
||||
case 'primary':
|
||||
color = '#0d6efd';
|
||||
break;
|
||||
case 'secondary':
|
||||
color = '#6c757d';
|
||||
break;
|
||||
case 'warning':
|
||||
color = '#ffc720';
|
||||
break;
|
||||
case 'success':
|
||||
color = '#13653f';
|
||||
break;
|
||||
case 'info':
|
||||
color = '#25cff2';
|
||||
break;
|
||||
case 'error':
|
||||
case 'danger':
|
||||
color = '#a52834';
|
||||
break;
|
||||
case 'light':
|
||||
color = '#babbbc';
|
||||
break;
|
||||
case 'dark':
|
||||
color = '#373b3e';
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn('variant not found');
|
||||
color: 'white';
|
||||
}
|
||||
return color;
|
||||
};
|
||||
|
||||
const show = () => {
|
||||
Toastify({
|
||||
text: text,
|
||||
duration: 2000,
|
||||
className: variant,
|
||||
gravity: 'top', // `top` or `bottom`
|
||||
position: 'center', // `left`, `center` or `right`
|
||||
stopOnFocus: true, // Prevents dismissing of toast on hover
|
||||
style: {
|
||||
background: getColorByName(variant),
|
||||
},
|
||||
}).showToast();
|
||||
};
|
||||
|
||||
return {
|
||||
show,
|
||||
};
|
||||
};
|
||||
|
||||
export default Toast;
|
||||
@@ -0,0 +1 @@
|
||||
@import 'toastify-1.12/toastify';
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*!
|
||||
* Toastify js 1.12.0
|
||||
* https://github.com/apvarun/toastify-js
|
||||
* @license MIT licensed
|
||||
*
|
||||
* Copyright (C) 2018 Varun A P
|
||||
*/
|
||||
|
||||
.toastify {
|
||||
padding: 12px 20px;
|
||||
color: #ffffff;
|
||||
display: inline-block;
|
||||
box-shadow:
|
||||
0 3px 6px -1px rgba(0, 0, 0, 0.12),
|
||||
0 10px 36px -4px rgba(77, 96, 232, 0.3);
|
||||
background: -webkit-linear-gradient(315deg, #73a5ff, #5477f5);
|
||||
background: linear-gradient(135deg, #73a5ff, #5477f5);
|
||||
position: fixed;
|
||||
opacity: 0;
|
||||
transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
max-width: calc(50% - 20px);
|
||||
z-index: 2147483647;
|
||||
}
|
||||
|
||||
.toastify.on {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.toast-close {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-size: 1em;
|
||||
opacity: 0.4;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.toastify-right {
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
.toastify-left {
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
.toastify-top {
|
||||
top: -150px;
|
||||
}
|
||||
|
||||
.toastify-bottom {
|
||||
bottom: -150px;
|
||||
}
|
||||
|
||||
.toastify-rounded {
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.toastify-avatar {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
margin: -7px 5px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.toastify-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-width: fit-content;
|
||||
max-width: -moz-fit-content;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 360px) {
|
||||
.toastify-right,
|
||||
.toastify-left {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-width: fit-content;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user