This commit is contained in:
1
03_dom/unterricht/tag30/README.md
Normal file
1
03_dom/unterricht/tag30/README.md
Normal file
@@ -0,0 +1 @@
|
||||
**Exkurs: Finalisierung Webseite mit Express + ProduktManager nach CRUD + Verwendung von Modulen**
|
||||
@@ -38,9 +38,29 @@ Content-Type: application/json
|
||||
|
||||
# DELETE Product
|
||||
|
||||
DELETE http://127.0.0.1:8000/api/products/940c316b-518c-4a18-b1f6-328e010eef4d
|
||||
DELETE http://127.0.0.1:8000/api/products/6cbe4783-cfb5-4ed7-8196-9d6b55217de0
|
||||
Content-Type: application/json
|
||||
|
||||
###
|
||||
|
||||
# RESET Products
|
||||
PATCH http://127.0.0.1:8000/api/products/reset
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"reset": true
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# SAVE Products
|
||||
PATCH http://127.0.0.1:8000/api/products/save
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"save": true
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
38
webseite/backend/data/products.bakup.json
Normal file
38
webseite/backend/data/products.bakup.json
Normal file
@@ -0,0 +1,38 @@
|
||||
[
|
||||
{
|
||||
"_id": "6cbe4783-cfb5-4ed7-8196-9d6b55217de0",
|
||||
"position": 1,
|
||||
"name": "3Doodler 3D Printing Pen",
|
||||
"price": 29.99
|
||||
},
|
||||
{
|
||||
"_id": "c38913d4-9524-461f-85d2-525241166e8e",
|
||||
"position": 2,
|
||||
"name": "Powerstation 5- E. Maximus Chargus",
|
||||
"price": 44.95
|
||||
},
|
||||
{
|
||||
"_id": "cdc887bd-fb0e-4485-8516-0a3720c3bb72",
|
||||
"position": 3,
|
||||
"name": "8-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 6.99
|
||||
},
|
||||
{
|
||||
"_id": "bded2710-8db8-41a5-87af-269195eab449",
|
||||
"position": 4,
|
||||
"name": "16-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 12.99
|
||||
},
|
||||
{
|
||||
"_id": "3b3352c1-789d-4b4f-8a93-f05f44ece5a5",
|
||||
"position": 5,
|
||||
"name": "32-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 18.99
|
||||
},
|
||||
{
|
||||
"_id": "5af39d33-cbcd-42c7-a160-50084faa91ba",
|
||||
"position": 6,
|
||||
"name": "64-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 21.99
|
||||
}
|
||||
]
|
||||
@@ -35,4 +35,4 @@
|
||||
"name": "64-Bit Legendary Hero Heat-Change Mug",
|
||||
"price": 21.99
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -14,6 +14,15 @@ const ProductModel = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
try {
|
||||
products = JSON.parse(fs.readFileSync('./data/products.bakup.json', 'utf-8'));
|
||||
} catch (error) {
|
||||
console.log('Something went wrong: ', error);
|
||||
products = [];
|
||||
}
|
||||
};
|
||||
|
||||
const save = () => {
|
||||
try {
|
||||
fs.writeFileSync('./data/products.json', JSON.stringify(products, null, 2), 'utf-8');
|
||||
@@ -53,7 +62,6 @@ const ProductModel = () => {
|
||||
};
|
||||
|
||||
const update = (product) => {
|
||||
console.log(product);
|
||||
const foundIdx = products.findIndex((obj) => {
|
||||
return obj._id === product._id;
|
||||
});
|
||||
@@ -98,6 +106,7 @@ const ProductModel = () => {
|
||||
get,
|
||||
update,
|
||||
delete: remove,
|
||||
reset,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"description": "",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "npx nodemon server.js",
|
||||
"server": "npx nodemon server.js"
|
||||
},
|
||||
"keywords": [],
|
||||
|
||||
@@ -13,7 +13,7 @@ const app = express();
|
||||
const products = ProductModel();
|
||||
|
||||
const corsOptions = {
|
||||
origin: 'http://localhost:3000',
|
||||
origin: ['http://localhost:3000', 'http://127.0.0.1:3000'],
|
||||
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
|
||||
};
|
||||
|
||||
@@ -22,7 +22,12 @@ products.load();
|
||||
|
||||
// Middleware
|
||||
app.use(cors(corsOptions)); // Fängt alle HTTP-Requests vor den Routenabfragen ab.
|
||||
app.use(express.json()); // HTTP-Request Body wird als JSON Objekt erwartet und geparsed wird.
|
||||
app.use(express.json()); // HTTP-Request Body wird als JSON Objekt erwartet und geparsed wird. // app.use(bodyParser.json()) - Modul body-parser war in express 4 notwendig
|
||||
|
||||
app.use((req, res, next) => {
|
||||
console.log(color.yellow('HTTP-Method: '), color.magenta(req.method));
|
||||
next();
|
||||
});
|
||||
|
||||
// Routes
|
||||
app.get('/', (req, res) => {
|
||||
@@ -101,6 +106,34 @@ app.put('/api/products', (req, res) => {
|
||||
return res.send({ msg: 'Product updated', status: 200, success: true, data: product });
|
||||
});
|
||||
|
||||
// HTTP - Methode - PATCH
|
||||
// reseten von Produkten
|
||||
app.patch('/api/products/reset', (req, res) => {
|
||||
const reset = req.body.reset;
|
||||
|
||||
if (!reset) {
|
||||
return res.status(400).send({ msg: 'Could not reset products', status: 400, success: false });
|
||||
}
|
||||
|
||||
products.reset();
|
||||
|
||||
return res.send({ msg: 'Products reseted', status: 200, success: true, data: reset });
|
||||
});
|
||||
|
||||
// HTTP - Methode - PATCH
|
||||
// speichern von Produkten
|
||||
app.patch('/api/products/save', (req, res) => {
|
||||
const save = req.body.save;
|
||||
|
||||
if (!save) {
|
||||
return res.status(400).send({ msg: 'Could not save products', status: 400, success: false });
|
||||
}
|
||||
|
||||
products.save();
|
||||
|
||||
return res.send({ msg: 'Products saved', status: 200, success: true, data: save });
|
||||
});
|
||||
|
||||
// HTTP - Methode - DELETE
|
||||
// CRU(D) - delete
|
||||
// BREA(D) - delete
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,9 +1,344 @@
|
||||
(() => {
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __commonJS = (cb, mod) => function __require() {
|
||||
try {
|
||||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
||||
} catch (e) {
|
||||
throw mod = 0, e;
|
||||
}
|
||||
};
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
|
||||
// node_modules/toastify-js/src/toastify.js
|
||||
var require_toastify = __commonJS({
|
||||
"node_modules/toastify-js/src/toastify.js"(exports, module) {
|
||||
(function(root, factory) {
|
||||
if (typeof module === "object" && module.exports) {
|
||||
module.exports = factory();
|
||||
} else {
|
||||
root.Toastify = factory();
|
||||
}
|
||||
})(exports, function(global) {
|
||||
var Toastify2 = function(options) {
|
||||
return new Toastify2.lib.init(options);
|
||||
}, version = "1.12.0";
|
||||
Toastify2.defaults = {
|
||||
oldestFirst: true,
|
||||
text: "Toastify is awesome!",
|
||||
node: void 0,
|
||||
duration: 3e3,
|
||||
selector: void 0,
|
||||
callback: function() {
|
||||
},
|
||||
destination: void 0,
|
||||
newWindow: false,
|
||||
close: false,
|
||||
gravity: "toastify-top",
|
||||
positionLeft: false,
|
||||
position: "",
|
||||
backgroundColor: "",
|
||||
avatar: "",
|
||||
className: "",
|
||||
stopOnFocus: true,
|
||||
onClick: function() {
|
||||
},
|
||||
offset: { x: 0, y: 0 },
|
||||
escapeMarkup: true,
|
||||
ariaLive: "polite",
|
||||
style: { background: "" }
|
||||
};
|
||||
Toastify2.lib = Toastify2.prototype = {
|
||||
toastify: version,
|
||||
constructor: Toastify2,
|
||||
// Initializing the object with required parameters
|
||||
init: function(options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
this.options = {};
|
||||
this.toastElement = null;
|
||||
this.options.text = options.text || Toastify2.defaults.text;
|
||||
this.options.node = options.node || Toastify2.defaults.node;
|
||||
this.options.duration = options.duration === 0 ? 0 : options.duration || Toastify2.defaults.duration;
|
||||
this.options.selector = options.selector || Toastify2.defaults.selector;
|
||||
this.options.callback = options.callback || Toastify2.defaults.callback;
|
||||
this.options.destination = options.destination || Toastify2.defaults.destination;
|
||||
this.options.newWindow = options.newWindow || Toastify2.defaults.newWindow;
|
||||
this.options.close = options.close || Toastify2.defaults.close;
|
||||
this.options.gravity = options.gravity === "bottom" ? "toastify-bottom" : Toastify2.defaults.gravity;
|
||||
this.options.positionLeft = options.positionLeft || Toastify2.defaults.positionLeft;
|
||||
this.options.position = options.position || Toastify2.defaults.position;
|
||||
this.options.backgroundColor = options.backgroundColor || Toastify2.defaults.backgroundColor;
|
||||
this.options.avatar = options.avatar || Toastify2.defaults.avatar;
|
||||
this.options.className = options.className || Toastify2.defaults.className;
|
||||
this.options.stopOnFocus = options.stopOnFocus === void 0 ? Toastify2.defaults.stopOnFocus : options.stopOnFocus;
|
||||
this.options.onClick = options.onClick || Toastify2.defaults.onClick;
|
||||
this.options.offset = options.offset || Toastify2.defaults.offset;
|
||||
this.options.escapeMarkup = options.escapeMarkup !== void 0 ? options.escapeMarkup : Toastify2.defaults.escapeMarkup;
|
||||
this.options.ariaLive = options.ariaLive || Toastify2.defaults.ariaLive;
|
||||
this.options.style = options.style || Toastify2.defaults.style;
|
||||
if (options.backgroundColor) {
|
||||
this.options.style.background = options.backgroundColor;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
// Building the DOM element
|
||||
buildToast: function() {
|
||||
if (!this.options) {
|
||||
throw "Toastify is not initialized";
|
||||
}
|
||||
var divElement = document.createElement("div");
|
||||
divElement.className = "toastify on " + this.options.className;
|
||||
if (!!this.options.position) {
|
||||
divElement.className += " toastify-" + this.options.position;
|
||||
} else {
|
||||
if (this.options.positionLeft === true) {
|
||||
divElement.className += " toastify-left";
|
||||
console.warn("Property `positionLeft` will be depreciated in further versions. Please use `position` instead.");
|
||||
} else {
|
||||
divElement.className += " toastify-right";
|
||||
}
|
||||
}
|
||||
divElement.className += " " + this.options.gravity;
|
||||
if (this.options.backgroundColor) {
|
||||
console.warn('DEPRECATION NOTICE: "backgroundColor" is being deprecated. Please use the "style.background" property.');
|
||||
}
|
||||
for (var property in this.options.style) {
|
||||
divElement.style[property] = this.options.style[property];
|
||||
}
|
||||
if (this.options.ariaLive) {
|
||||
divElement.setAttribute("aria-live", this.options.ariaLive);
|
||||
}
|
||||
if (this.options.node && this.options.node.nodeType === Node.ELEMENT_NODE) {
|
||||
divElement.appendChild(this.options.node);
|
||||
} else {
|
||||
if (this.options.escapeMarkup) {
|
||||
divElement.innerText = this.options.text;
|
||||
} else {
|
||||
divElement.innerHTML = this.options.text;
|
||||
}
|
||||
if (this.options.avatar !== "") {
|
||||
var avatarElement = document.createElement("img");
|
||||
avatarElement.src = this.options.avatar;
|
||||
avatarElement.className = "toastify-avatar";
|
||||
if (this.options.position == "left" || this.options.positionLeft === true) {
|
||||
divElement.appendChild(avatarElement);
|
||||
} else {
|
||||
divElement.insertAdjacentElement("afterbegin", avatarElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.options.close === true) {
|
||||
var closeElement = document.createElement("button");
|
||||
closeElement.type = "button";
|
||||
closeElement.setAttribute("aria-label", "Close");
|
||||
closeElement.className = "toast-close";
|
||||
closeElement.innerHTML = "✖";
|
||||
closeElement.addEventListener(
|
||||
"click",
|
||||
function(event) {
|
||||
event.stopPropagation();
|
||||
this.removeElement(this.toastElement);
|
||||
window.clearTimeout(this.toastElement.timeOutValue);
|
||||
}.bind(this)
|
||||
);
|
||||
var width = window.innerWidth > 0 ? window.innerWidth : screen.width;
|
||||
if ((this.options.position == "left" || this.options.positionLeft === true) && width > 360) {
|
||||
divElement.insertAdjacentElement("afterbegin", closeElement);
|
||||
} else {
|
||||
divElement.appendChild(closeElement);
|
||||
}
|
||||
}
|
||||
if (this.options.stopOnFocus && this.options.duration > 0) {
|
||||
var self = this;
|
||||
divElement.addEventListener(
|
||||
"mouseover",
|
||||
function(event) {
|
||||
window.clearTimeout(divElement.timeOutValue);
|
||||
}
|
||||
);
|
||||
divElement.addEventListener(
|
||||
"mouseleave",
|
||||
function() {
|
||||
divElement.timeOutValue = window.setTimeout(
|
||||
function() {
|
||||
self.removeElement(divElement);
|
||||
},
|
||||
self.options.duration
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
if (typeof this.options.destination !== "undefined") {
|
||||
divElement.addEventListener(
|
||||
"click",
|
||||
function(event) {
|
||||
event.stopPropagation();
|
||||
if (this.options.newWindow === true) {
|
||||
window.open(this.options.destination, "_blank");
|
||||
} else {
|
||||
window.location = this.options.destination;
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
if (typeof this.options.onClick === "function" && typeof this.options.destination === "undefined") {
|
||||
divElement.addEventListener(
|
||||
"click",
|
||||
function(event) {
|
||||
event.stopPropagation();
|
||||
this.options.onClick();
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
if (typeof this.options.offset === "object") {
|
||||
var x = getAxisOffsetAValue("x", this.options);
|
||||
var y = getAxisOffsetAValue("y", this.options);
|
||||
var xOffset = this.options.position == "left" ? x : "-" + x;
|
||||
var yOffset = this.options.gravity == "toastify-top" ? y : "-" + y;
|
||||
divElement.style.transform = "translate(" + xOffset + "," + yOffset + ")";
|
||||
}
|
||||
return divElement;
|
||||
},
|
||||
// Displaying the toast
|
||||
showToast: function() {
|
||||
this.toastElement = this.buildToast();
|
||||
var rootElement;
|
||||
if (typeof this.options.selector === "string") {
|
||||
rootElement = document.getElementById(this.options.selector);
|
||||
} else if (this.options.selector instanceof HTMLElement || typeof ShadowRoot !== "undefined" && this.options.selector instanceof ShadowRoot) {
|
||||
rootElement = this.options.selector;
|
||||
} else {
|
||||
rootElement = document.body;
|
||||
}
|
||||
if (!rootElement) {
|
||||
throw "Root element is not defined";
|
||||
}
|
||||
var elementToInsert = Toastify2.defaults.oldestFirst ? rootElement.firstChild : rootElement.lastChild;
|
||||
rootElement.insertBefore(this.toastElement, elementToInsert);
|
||||
Toastify2.reposition();
|
||||
if (this.options.duration > 0) {
|
||||
this.toastElement.timeOutValue = window.setTimeout(
|
||||
function() {
|
||||
this.removeElement(this.toastElement);
|
||||
}.bind(this),
|
||||
this.options.duration
|
||||
);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
hideToast: function() {
|
||||
if (this.toastElement.timeOutValue) {
|
||||
clearTimeout(this.toastElement.timeOutValue);
|
||||
}
|
||||
this.removeElement(this.toastElement);
|
||||
},
|
||||
// Removing the element from the DOM
|
||||
removeElement: function(toastElement) {
|
||||
toastElement.className = toastElement.className.replace(" on", "");
|
||||
window.setTimeout(
|
||||
function() {
|
||||
if (this.options.node && this.options.node.parentNode) {
|
||||
this.options.node.parentNode.removeChild(this.options.node);
|
||||
}
|
||||
if (toastElement.parentNode) {
|
||||
toastElement.parentNode.removeChild(toastElement);
|
||||
}
|
||||
this.options.callback.call(toastElement);
|
||||
Toastify2.reposition();
|
||||
}.bind(this),
|
||||
400
|
||||
);
|
||||
}
|
||||
};
|
||||
Toastify2.reposition = function() {
|
||||
var topLeftOffsetSize = {
|
||||
top: 15,
|
||||
bottom: 15
|
||||
};
|
||||
var topRightOffsetSize = {
|
||||
top: 15,
|
||||
bottom: 15
|
||||
};
|
||||
var offsetSize = {
|
||||
top: 15,
|
||||
bottom: 15
|
||||
};
|
||||
var allToasts = document.getElementsByClassName("toastify");
|
||||
var classUsed;
|
||||
for (var i = 0; i < allToasts.length; i++) {
|
||||
if (containsClass(allToasts[i], "toastify-top") === true) {
|
||||
classUsed = "toastify-top";
|
||||
} else {
|
||||
classUsed = "toastify-bottom";
|
||||
}
|
||||
var height = allToasts[i].offsetHeight;
|
||||
classUsed = classUsed.substr(9, classUsed.length - 1);
|
||||
var offset2 = 15;
|
||||
var width = window.innerWidth > 0 ? window.innerWidth : screen.width;
|
||||
if (width <= 360) {
|
||||
allToasts[i].style[classUsed] = offsetSize[classUsed] + "px";
|
||||
offsetSize[classUsed] += height + offset2;
|
||||
} else {
|
||||
if (containsClass(allToasts[i], "toastify-left") === true) {
|
||||
allToasts[i].style[classUsed] = topLeftOffsetSize[classUsed] + "px";
|
||||
topLeftOffsetSize[classUsed] += height + offset2;
|
||||
} else {
|
||||
allToasts[i].style[classUsed] = topRightOffsetSize[classUsed] + "px";
|
||||
topRightOffsetSize[classUsed] += height + offset2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
function getAxisOffsetAValue(axis, options) {
|
||||
if (options.offset[axis]) {
|
||||
if (isNaN(options.offset[axis])) {
|
||||
return options.offset[axis];
|
||||
} else {
|
||||
return options.offset[axis] + "px";
|
||||
}
|
||||
}
|
||||
return "0px";
|
||||
}
|
||||
function containsClass(elem, yourClass) {
|
||||
if (!elem || typeof yourClass !== "string") {
|
||||
return false;
|
||||
} else if (elem.className && elem.className.trim().split(/\s+/gi).indexOf(yourClass) > -1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Toastify2.lib.init.prototype = Toastify2.lib;
|
||||
return Toastify2;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// node_modules/@popperjs/core/lib/index.js
|
||||
var lib_exports = {};
|
||||
@@ -5164,6 +5499,65 @@
|
||||
enableDismissTrigger(Toast);
|
||||
defineJQueryPlugin(Toast);
|
||||
|
||||
// dev/scripts/modules/Toast.js
|
||||
var import_toastify_js = __toESM(require_toastify(), 1);
|
||||
var Toast2 = (text = "", variant = "primary") => {
|
||||
const getColorByName = (variant2 = "") => {
|
||||
let color;
|
||||
switch (variant2.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 = () => {
|
||||
(0, import_toastify_js.default)({
|
||||
text,
|
||||
duration: 2e3,
|
||||
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
|
||||
};
|
||||
};
|
||||
var Toast_default = Toast2;
|
||||
|
||||
// dev/scripts/modules/ProductManager.js
|
||||
var ProductManager = (el = null) => {
|
||||
const BASE_URL = "http://127.0.0.1:8000";
|
||||
@@ -5177,25 +5571,41 @@
|
||||
inputPrice: module.querySelector(".input-product-price"),
|
||||
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")
|
||||
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
|
||||
});
|
||||
const init = () => {
|
||||
console.log("init");
|
||||
initProducts();
|
||||
DOM.btnAdd.addEventListener("click", onClickAdd);
|
||||
DOM.btnAdd.disabled = true;
|
||||
DOM.btnConfirmDelete.addEventListener("click", onClickConfirmDelete);
|
||||
DOM.formEdit.addEventListener("submit", onSubmitEdit);
|
||||
window.addEventListener("keyup", onKeyUp);
|
||||
};
|
||||
const onKeyUp = (e) => {
|
||||
DOM.btnAdd.disabled = DOM.inputName.value === "" || DOM.inputPrice.value === "";
|
||||
};
|
||||
const onSubmitEdit = (e) => {
|
||||
e.preventDefault();
|
||||
@@ -5205,11 +5615,17 @@
|
||||
_id: DOM.inputEditId.value,
|
||||
position: DOM.inputEditPosition.value
|
||||
};
|
||||
DOM.btnUpdate.querySelector("i").classList.add("fa-spin");
|
||||
updateProduct(product).then((data) => {
|
||||
console.log(data);
|
||||
if (data.success) {
|
||||
Toast_default(data.msg, "success").show();
|
||||
DOM.btnUpdate.querySelector("i").classList.remove("fa-spin");
|
||||
loadProducts();
|
||||
resetFields();
|
||||
bsModalEdit.hide();
|
||||
} else {
|
||||
Toast_default(data.msg, "error").show();
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -5223,7 +5639,9 @@
|
||||
console.log(data);
|
||||
if (data.success) {
|
||||
console.log("add");
|
||||
Toast_default(data.msg, "success").show();
|
||||
loadProducts();
|
||||
resetFields();
|
||||
}
|
||||
});
|
||||
disableNonFunctionalButtons();
|
||||
@@ -5238,9 +5656,25 @@
|
||||
};
|
||||
const onClickRemove = (e) => {
|
||||
const btnEl = e.currentTarget;
|
||||
const currentRowEl = parents(btnEl, "tr")[0];
|
||||
currentRowEl.remove();
|
||||
disableNonFunctionalButtons();
|
||||
const currentRow = parents(btnEl, "tr")[0];
|
||||
const id = currentRow.dataset.id;
|
||||
const productName = currentRow.querySelector(".td-name").textContent.trim();
|
||||
DOM.btnConfirmDelete.dataset.id = id;
|
||||
DOM.productName.textContent = productName;
|
||||
};
|
||||
const onClickConfirmDelete = (e) => {
|
||||
const btnEl = e.currentTarget;
|
||||
const id = btnEl.dataset.id;
|
||||
deleteProduct(id).then((data) => {
|
||||
if (data.success) {
|
||||
Toast_default(data.msg, "success").show();
|
||||
bsModalDelete.hide();
|
||||
loadProducts();
|
||||
} else {
|
||||
Toast_default(data.msg, "error").show();
|
||||
console.error(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
const onClickMoveUp = (e) => {
|
||||
const btnEl = e.currentTarget;
|
||||
@@ -5307,6 +5741,7 @@
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
Toast_default(error, "error").show();
|
||||
console.error(error);
|
||||
return error;
|
||||
}
|
||||
@@ -5356,11 +5791,30 @@
|
||||
return error;
|
||||
}
|
||||
};
|
||||
const deleteProduct = async (id) => {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/api/products/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error("Fetch went wrong.");
|
||||
}
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return error;
|
||||
}
|
||||
};
|
||||
const initProducts = () => {
|
||||
getProducts().then((products) => {
|
||||
products.forEach((product) => {
|
||||
addProduct(product);
|
||||
});
|
||||
Toast_default("Init products").show();
|
||||
disableNonFunctionalButtons();
|
||||
});
|
||||
};
|
||||
@@ -5405,6 +5859,12 @@
|
||||
DOM.inputEditId.value = id;
|
||||
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");
|
||||
@@ -5423,7 +5883,8 @@
|
||||
init();
|
||||
return {
|
||||
init,
|
||||
initProducts
|
||||
initProducts,
|
||||
resetFields
|
||||
};
|
||||
};
|
||||
var ProductManager_default = ProductManager;
|
||||
@@ -5456,6 +5917,15 @@
|
||||
})();
|
||||
/*! Bundled license information:
|
||||
|
||||
toastify-js/src/toastify.js:
|
||||
(*!
|
||||
* Toastify js 1.12.0
|
||||
* https://github.com/apvarun/toastify-js
|
||||
* @license MIT licensed
|
||||
*
|
||||
* Copyright (C) 2018 Varun A P
|
||||
*)
|
||||
|
||||
bootstrap/dist/js/bootstrap.esm.js:
|
||||
(*!
|
||||
* Bootstrap v5.3.8 (https://getbootstrap.com/)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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;
|
||||
}
|
||||
}
|
||||
9
webseite/frontend/package-lock.json
generated
9
webseite/frontend/package-lock.json
generated
@@ -9,7 +9,8 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bootstrap": "^5.3.8"
|
||||
"bootstrap": "^5.3.8",
|
||||
"toastify-js": "^1.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"browser-sync": "^3.0.4",
|
||||
@@ -4369,6 +4370,12 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toastify-js": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/toastify-js/-/toastify-js-1.12.0.tgz",
|
||||
"integrity": "sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "run-p server css js",
|
||||
"js": "npx esbuild dev/scripts/main.js --watch --bundle --sourcemap --outfile=assets/js/build.js",
|
||||
"http-server": "npx http-server -p 3000 -c-1",
|
||||
"server": "npx browser-sync start --server --files 'assets/css/*.css, assets/js/*.js, **/*.html, *.html'",
|
||||
@@ -21,6 +22,7 @@
|
||||
"sass": "^1.101.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "^5.3.8"
|
||||
"bootstrap": "^5.3.8",
|
||||
"toastify-js": "^1.12.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,23 @@
|
||||
<!-- ▼ product-manager ▼ -->
|
||||
<div class="product-manager">
|
||||
<div class="container">
|
||||
<nav class="nav-products-actions">
|
||||
<ul class="list">
|
||||
<li>
|
||||
<button class="btn btn-danger button-products-reset">
|
||||
<i class="fa-solid fa-box-archive"></i>
|
||||
Reset Products
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-dark button-products-save">
|
||||
<i class="fa-solid fa-hard-drive"></i>
|
||||
Save Products
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- table.table.table-striped.table-products>(thead.table-dark>tr>th{Name}+th{Price in €})+tbody>tr>td*2 -->
|
||||
<table class="table table-striped table-products">
|
||||
<thead class="table-dark">
|
||||
@@ -119,7 +136,10 @@
|
||||
<i class="fas fa-file-pen"></i>
|
||||
<span class="visually-hidden">Edit Product</span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger button-product-remove">
|
||||
<button
|
||||
class="btn btn-sm btn-danger button-product-remove"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#modal-delete">
|
||||
<i class="fas fa-trash-can"></i>
|
||||
<span class="visually-hidden">Remove Product</span>
|
||||
</button>
|
||||
@@ -188,6 +208,34 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- ▲ /Modal ▲ -->
|
||||
|
||||
<!-- Modal Delete -->
|
||||
<div
|
||||
class="modal modal-delete fade"
|
||||
id="modal-delete"
|
||||
tabindex="-1"
|
||||
aria-labelledby="modal-delete-label"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="modal-delete-label">Delete Product</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
Do you really want to delete Product:<br />
|
||||
<strong class="product-name">PRODUCT_NAME</strong>
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-danger button-confirm-delete">Confirm to delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ▲ /Modal Delete ▲ -->
|
||||
</div>
|
||||
<!-- ▲ /product-manager ▲ -->
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user