cake-order-tracker/server/static/order.js
bronkuu 5f156ab6a0 blocked dates page and warning for special-cake orders
Add blocked_date table (migration 5), model, and store CRUD methods.
Add management page at /blocked with add/delete functionality.
Add nav link 'Terminy zablokowane' with event_busy icon.
Pass blocked dates to order form as JSON data attribute.
On form submit, if the order has special cakes and the date is
blocked, show a confirm() dialog warning the user.
2026-06-15 18:54:48 +02:00

279 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var editingSpecialId = null;
function openSpecialCakeOverlay() {
editingSpecialId = null;
document.getElementById("sc-overlay-title").textContent = "Dodaj tort specjalny";
document.getElementById("sc-name").value = "";
document.getElementById("sc-price").value = "";
document.getElementById("sc-size").value = "";
document.getElementById("sc-shape").value = "";
document.getElementById("sc-flavour").value = "";
document.getElementById("sc-notes").value = "";
document.getElementById("sc-new-tag").value = "";
var cbs = document.querySelectorAll(".sc-tag-cb");
for (var i = 0; i < cbs.length; i++) cbs[i].checked = false;
document.getElementById("sc-overlay").classList.add("open");
document.getElementById("sc-name").focus();
}
function closeSpecialCakeOverlay() {
document.getElementById("sc-overlay").classList.remove("open");
editingSpecialId = null;
}
function getOverlayFormData() {
return {
name: document.getElementById("sc-name").value.trim(),
price: document.getElementById("sc-price").value,
size: document.getElementById("sc-size").value.trim(),
shape: document.getElementById("sc-shape").value,
flavour: document.getElementById("sc-flavour").value.trim(),
notes: document.getElementById("sc-notes").value.trim(),
tagIds: (function() {
var ids = [];
var cbs = document.querySelectorAll(".sc-tag-cb:checked");
for (var i = 0; i < cbs.length; i++) ids.push(cbs[i].value);
return ids;
})(),
tagNames: (function() {
var names = [];
var cbs = document.querySelectorAll(".sc-tag-cb:checked");
for (var i = 0; i < cbs.length; i++)
names.push(cbs[i].closest(".tag-chip").textContent.trim());
return names;
})(),
newTag: document.getElementById("sc-new-tag").value.trim()
};
}
function buildSpecialCakeHTML(d) {
var parts = [];
if (d.size) parts.push(d.size);
if (d.flavour) parts.push(d.flavour);
if (d.shape === "round") parts.push("okragly");
else if (d.shape === "square") parts.push("kwadrat");
if (d.notes) parts.push("notatka: " + d.notes);
var detail = parts.length ? parts.join(", ") : "";
var tagHtml = d.tagNames.length > 0 || d.newTag ? '<div style="margin-top:0.25rem;display:flex;flex-wrap:wrap;gap:0.125rem">' : "";
for (var i = 0; i < d.tagNames.length; i++)
tagHtml += '<span class="tag-chip" style="font-size:0.625rem;padding:0.125rem 0.375rem">' + d.tagNames[i] + "</span>";
if (d.newTag)
tagHtml += '<span class="tag-chip" style="font-size:0.625rem;padding:0.125rem 0.375rem">' + d.newTag + "</span>";
if (d.tagNames.length > 0 || d.newTag) tagHtml += "</div>";
var priceVal = Number(d.price).toFixed(2);
var tagIdsStr = d.tagIds.join(",");
var html =
'<div style="flex:1;min-width:0;padding-right:0.5rem">' +
'<div class="name">' + d.name + '</div>' +
'<div class="detail">' + detail + '</div>' +
tagHtml +
'</div>' +
'<div class="price">' + priceVal + ' PLN</div>' +
'<div style="display:flex;flex-direction:column;gap:0.125rem">' +
'<button type="button" class="action-btn" onclick="editSpecialCake(this)" title="Edytuj">' +
'<span class="material-symbols-outlined" style="font-size:1rem">edit</span></button>' +
'<button type="button" class="remove" onclick="removeSpecialCake(this)" title="Usuń">' +
'<span class="material-symbols-outlined" style="font-size:1rem">close</span></button>' +
'</div>' +
'<input type="hidden" name="special_name[]" value="' + d.name + '">' +
'<input type="hidden" name="special_price[]" value="' + d.price + '">' +
'<input type="hidden" name="special_size[]" value="' + d.size + '">' +
'<input type="hidden" name="special_shape[]" value="' + d.shape + '">' +
'<input type="hidden" name="special_flavour[]" value="' + d.flavour + '">' +
'<input type="hidden" name="special_notes[]" value="' + d.notes + '">' +
'<input type="hidden" name="special_tags[]" value="' + tagIdsStr + '">';
if (d.newTag)
html += '<input type="hidden" name="special_new_tag[]" value="' + d.newTag + '">';
return html;
}
function saveSpecialCake() {
var d = getOverlayFormData();
if (!d.name) return;
if (editingSpecialId) {
var item = document.querySelector('#basket-items li[data-special-id="' + editingSpecialId + '"]');
if (item) {
item.innerHTML = buildSpecialCakeHTML(d);
}
} else {
d.id = Date.now();
var item = document.createElement("li");
item.setAttribute("data-special-id", d.id);
item.innerHTML = buildSpecialCakeHTML(d);
document.getElementById("basket-items").appendChild(item);
}
closeSpecialCakeOverlay();
updateTotals();
}
function editSpecialCake(btn) {
var item = btn.closest("li");
var id = item.getAttribute("data-special-id");
editingSpecialId = id;
document.getElementById("sc-overlay-title").textContent = "Edytuj tort specjalny";
var hidden = item.querySelectorAll('input[type="hidden"]');
var vals = {};
for (var i = 0; i < hidden.length; i++) {
var name = hidden[i].getAttribute("name");
vals[name] = hidden[i].value;
}
document.getElementById("sc-name").value = vals["special_name[]"] || "";
document.getElementById("sc-price").value = vals["special_price[]"] || "";
document.getElementById("sc-size").value = vals["special_size[]"] || "";
document.getElementById("sc-shape").value = vals["special_shape[]"] || "";
document.getElementById("sc-flavour").value = vals["special_flavour[]"] || "";
document.getElementById("sc-notes").value = vals["special_notes[]"] || "";
var currentTagIds = (vals["special_tags[]"] || "").split(",").filter(function(x) { return x; });
document.getElementById("sc-new-tag").value = "";
var cbs = document.querySelectorAll(".sc-tag-cb");
for (var i = 0; i < cbs.length; i++) {
cbs[i].checked = currentTagIds.indexOf(cbs[i].value) >= 0;
}
document.getElementById("sc-overlay").classList.add("open");
document.getElementById("sc-name").focus();
}
function removeSpecialCake(btn) {
var item = btn.closest("li");
item.remove();
updateTotals();
}
function changeCake(id, name, price, delta) {
var input = document.querySelector('[name="cake[' + id + ']"]');
if (input != null) {
var qty = Number(input.value) + delta;
if (qty <= 0) {
var item = input.closest("li");
if (item) item.remove();
updateTotals();
updateCatalogueQty(id, 0);
return;
}
input.value = qty;
var item = input.closest("li");
item.querySelector(".qty").innerText = qty;
item.querySelector(".price").innerText = price * qty + " PLN";
updateTotals();
updateCatalogueQty(id, qty);
return;
}
if (delta <= 0) return;
var item = document.createElement("li");
item.setAttribute("data-cake-id", id);
item.innerHTML =
'<div style="flex:1;min-width:0;padding-right:0.5rem">' +
'<div class="name">' +
name +
"</div>" +
'<div class="detail">' +
price +
' PLN × <span class="qty">1</span></div>' +
"</div>" +
'<div class="price">' +
price +
" PLN</div>" +
'<button type="button" class="remove" onclick="removeCake(this)">' +
'<span class="material-symbols-outlined">close</span>' +
"</button>" +
'<input type="hidden" name="cake[' +
id +
']" value="1">';
document.getElementById("basket-items").appendChild(item);
updateTotals();
updateCatalogueQty(id, 1);
}
function removeCake(btn) {
var item = btn.closest("li");
var id = item.getAttribute("data-cake-id");
item.remove();
updateTotals();
updateCatalogueQty(id, 0);
}
var subtotal = 0;
function updatePrepaid() {
var paid = Number(document.querySelector('[name="paid"]')?.value || 0);
document.getElementById("paid-amount").innerText = paid.toFixed(2) + " PLN";
document.getElementById("total-price").innerText = (subtotal - paid).toFixed(2) + " PLN";
}
function updateTotals() {
subtotal = 0;
var items = document.querySelectorAll("#basket-items li");
items.forEach(function (item) {
var priceText = item.querySelector(".price").innerText;
var price = Number(priceText.split(" ")[0]);
if (item.hasAttribute("data-special-id")) {
subtotal += price;
} else {
var qty = Number(item.querySelector(".qty").innerText);
subtotal += price * qty;
}
});
document.getElementById("subtotal-price").innerText = subtotal.toFixed(2) + " PLN";
updatePrepaid();
}
function updateCatalogueQty(id, qty) {
var el = document.getElementById("qty-" + id);
if (el) el.innerText = qty;
}
var activeCatalogueTags = [];
function toggleCatalogueTagFilter(btn) {
var tag = btn.getAttribute("data-tag");
var idx = activeCatalogueTags.indexOf(tag);
if (idx >= 0) {
activeCatalogueTags.splice(idx, 1);
btn.classList.remove("active");
} else {
activeCatalogueTags.push(tag);
btn.classList.add("active");
}
applyCatalogueFilters();
}
function getCatalogueTextQuery() {
var input = document.querySelector(
'#catalogue-grid').closest('.card').querySelector('input[type="text"]'
);
return input ? input.value.toLowerCase() : "";
}
function applyCatalogueFilters() {
var query = getCatalogueTextQuery();
var items = document.querySelectorAll("#catalogue-grid > li");
items.forEach(function (item) {
var name = item.getAttribute("data-name").toLowerCase();
var matchesText = name.includes(query);
var matchesTags = true;
if (activeCatalogueTags.length > 0) {
var itemTags = (item.getAttribute("data-tags") || "").split(",");
for (var i = 0; i < activeCatalogueTags.length; i++) {
if (itemTags.indexOf(activeCatalogueTags[i]) < 0) {
matchesTags = false;
break;
}
}
}
item.style.display = matchesText && matchesTags ? "" : "none";
});
}
function filterCatalogue(input) {
applyCatalogueFilters();
}
function confirmBlockedDate(form) {
var date = form.querySelector('[name="date"]').value;
var blocked = JSON.parse(form.getAttribute("data-blocked-dates") || "[]");
var hasSpecial = form.querySelector('[name="special_name[]"]');
if (hasSpecial && blocked.indexOf(date) >= 0) {
return confirm("Data " + date + " jest zablokowana dla zamówień z tortami specjalnymi. Czy na pewno chcesz kontynuować?");
}
return true;
}