Abo Einmalprodukte und Bestätigung abschließen

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin 2026-06-05 15:28:08 +00:00
parent 2bdc9ada3c
commit 2269ce031f
57 changed files with 3647 additions and 371 deletions

297
public/js/iq-abo-onetime.js Normal file
View file

@ -0,0 +1,297 @@
var IqAboOneTime = {
card: "#onetime_order_card",
holder: "#insert_show_onetime_order",
abo_holder: "#insert_show_products_order",
summary_card: "#combined_summary_card",
comp_holder: "#holder_html_view_comp_product",
modal: "#modals-load-content",
oTable: null,
url: null,
btn_modal_add: ".add-onetime-product-basket",
btn_add: ".onetime-add-from-basket",
btn_remove: ".onetime-remove-from-basket",
input_change: ".onetime-input-onchange",
remove_item: ".remove_onetime_from_cart",
confirm_changes: ".onetime-confirm-changes",
discard_changes: ".onetime-discard-changes",
summary_col: "#combined_summary_col",
sticky_gap: 16,
_stickyTicking: false,
init: function () {
var _self = this;
_self.url = $(_self.card).data("onetime-route");
_self.reInitCart();
_self.initSticky();
return _self;
},
initSticky: function () {
var _self = this;
var card = document.querySelector(_self.summary_card);
var col = document.querySelector(_self.summary_col);
if (!card || !col) {
return;
}
var onScrollOrResize = function () {
if (_self._stickyTicking) {
return;
}
_self._stickyTicking = true;
window.requestAnimationFrame(function () {
_self.updateSticky(card, col);
_self._stickyTicking = false;
});
};
// Capture-Phase, damit Scroll-Events beliebiger Scroll-Container erfasst werden.
window.addEventListener("scroll", onScrollOrResize, true);
window.addEventListener("resize", onScrollOrResize);
_self.updateSticky(card, col);
},
resetSticky: function (card) {
card.style.position = "";
card.style.top = "";
card.style.left = "";
card.style.width = "";
card.style.zIndex = "";
},
updateSticky: function (card, col) {
var _self = this;
// Nur im zweispaltigen Layout (lg+) kleben.
if (window.innerWidth < 992) {
_self.resetSticky(card);
return;
}
var colStyle = window.getComputedStyle(col);
var padLeft = parseFloat(colStyle.paddingLeft) || 0;
var padRight = parseFloat(colStyle.paddingRight) || 0;
var colRect = col.getBoundingClientRect();
var contentLeft = colRect.left + padLeft;
var contentWidth = col.clientWidth - padLeft - padRight;
var cardHeight = card.offsetHeight;
var gap = _self.sticky_gap;
if (colRect.top >= gap) {
// Noch nicht über den oberen Rand gescrollt -> normaler Fluss.
_self.resetSticky(card);
return;
}
// Oben anheften, aber maximal bis zum Ende der Spalte.
var topPos = gap;
var maxTop = colRect.bottom - cardHeight;
if (maxTop < gap) {
topPos = maxTop;
}
card.style.position = "fixed";
card.style.top = topPos + "px";
card.style.left = contentLeft + "px";
card.style.width = contentWidth + "px";
card.style.zIndex = "1020";
},
setDatabase: function (oTable) {
this.oTable = oTable;
},
reInitModal: function () {
var _self = this;
$(_self.oTable)
.find(_self.btn_modal_add)
.off("click")
.on("click", function () {
_self.addFromModal($(this));
});
},
reInitCart: function () {
var _self = this;
var $holder = $(_self.holder);
$holder
.find(_self.btn_add)
.off("click")
.on("click", function () {
_self.changeQty($(this), 1);
});
$holder
.find(_self.btn_remove)
.off("click")
.on("click", function () {
_self.changeQty($(this), -1);
});
$holder
.find(_self.input_change)
.off("change")
.on("change", function () {
_self.updateInput($(this));
});
$holder
.find(_self.remove_item)
.off("click")
.on("click", function (event) {
event.preventDefault();
_self.removeItem($(this));
});
$holder
.find(_self.confirm_changes)
.off("click")
.on("click", function () {
_self.confirmChanges();
});
$holder
.find(_self.discard_changes)
.off("click")
.on("click", function () {
_self.discardChanges();
});
},
addFromModal: function (_obj) {
var _self = this;
var $modal = $(_self.modal);
$modal.one("hidden.bs.modal", function () {
_self
.performRequest({
action: "add",
product_id: _obj.data("product-id"),
})
.done(_self.refreshView);
});
$modal.modal("hide");
},
changeQty: function (_obj, delta) {
var _self = this;
var itemId = _obj.data("onetime-item-id");
var input = $(_self.holder).find(
'input[name="onetime_qty_' + itemId + '"]'
);
var qty = _self.checkNumber(parseInt(input.val(), 10) + delta);
input.val(qty);
_self
.performRequest({
action: "update",
one_time_item_id: itemId,
qty: qty,
})
.done(_self.refreshView);
},
updateInput: function (_obj) {
var _self = this;
var qty = _self.checkNumber(parseInt(_obj.val(), 10));
_obj.val(qty);
_self
.performRequest({
action: "update",
one_time_item_id: _obj.data("onetime-item-id"),
qty: qty,
})
.done(_self.refreshView);
},
removeItem: function (_obj) {
var _self = this;
_self
.performRequest({
action: "remove",
one_time_item_id: _obj.data("onetime-item-id"),
})
.done(_self.refreshView);
},
confirmChanges: function () {
var _self = this;
_self
.performRequest({
action: "confirm",
})
.done(_self.refreshView);
},
discardChanges: function () {
var _self = this;
_self
.performRequest({
action: "discard",
})
.done(_self.refreshView);
},
refreshView: function (data) {
var _self = IqAboOneTime;
if (data.html_onetime) {
$(_self.holder).html(data.html_onetime);
}
if (data.html_abo) {
$(_self.abo_holder).html(data.html_abo);
}
if (data.html_summary) {
$(_self.summary_card).html(data.html_summary);
}
if (data.html_comp !== undefined) {
$(_self.comp_holder).html(data.html_comp);
}
$(_self.modal).modal("hide");
_self.reInitCart();
if (typeof iqModalCart !== "undefined" && iqModalCart.reInitCart) {
iqModalCart.reInitCart($(_self.abo_holder));
iqModalCart.reInitCart($(_self.comp_holder));
}
var card = document.querySelector(_self.summary_card);
var col = document.querySelector(_self.summary_col);
if (card && col) {
_self.updateSticky(card, col);
}
},
performRequest: function (data) {
var _self = this;
return $.ajax({
url: _self.url,
data: data,
type: "POST",
dataType: "json",
cache: false,
headers: {
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
},
}).fail(function (jqXHR) {
var msg =
jqXHR.responseJSON && jqXHR.responseJSON.message
? jqXHR.responseJSON.message
: "Error";
var errorEl = document.getElementById("insert_onetime_error_message");
if (!errorEl) {
errorEl = document.createElement("div");
errorEl.id = "insert_onetime_error_message";
errorEl.className = "alert alert-danger mt-2";
var holder = document.querySelector("#insert_show_onetime_order");
if (holder) {
holder.insertBefore(errorEl, holder.firstChild);
}
}
errorEl.textContent = msg;
});
},
checkNumber: function (number) {
if (number < 1 || isNaN(number)) {
return 1;
}
if (number > 100) {
return 100;
}
return number;
},
};