templates, handlers, CSS, JS: special cakes UI, HTMX nav, dashboard overhaul

Special cakes UI: overlay drawer, basket integration, catalogue tag
filter, form parsing, JS save/edit/remove.
HTMX navigation: MainContent component, nav links with hx-get,
handlers return partial on HX-Request.
Dashboard: metrics grid, CakeBreakdown, OrderRow with status select,
CakeCounts/OrderCounts, torty filter, tort badge, results-pane.
Collapsible sidebar: toggle button, localStorage, CSS transitions.
Location field: dropdown replaced with pill toggle.
Inline style cleanup: moved to CSS classes (tag-grid, grow, icon.sm,
header-actions, toggle-group, success-icon, etc.).
Dead CSS removal: pagination, status-chip, badge-pill, login-page.
Helpers: exported CakeTitle/OrderTitle, added tagNames, tagIDsComma,
orderItemsDesc, specialCakeDetail, removed unused helpers.
This commit is contained in:
bronkuu 2026-06-15 18:50:21 +02:00
parent 17b92a706a
commit 5a5372b831
18 changed files with 1153 additions and 422 deletions

View file

@ -1,3 +1,144 @@
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) {
@ -55,20 +196,24 @@ var subtotal = 0;
function updatePrepaid() {
var paid = Number(document.querySelector('[name="paid"]')?.value || 0);
document.getElementById("paid-amount").innerText = paid + " PLN";
document.getElementById("total-price").innerText = subtotal - paid + " PLN";
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(".detail").innerText;
var priceText = item.querySelector(".price").innerText;
var price = Number(priceText.split(" ")[0]);
var qty = Number(item.querySelector(".qty").innerText);
subtotal += price * qty;
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 + " PLN";
document.getElementById("subtotal-price").innerText = subtotal.toFixed(2) + " PLN";
updatePrepaid();
}
@ -77,11 +222,48 @@ function updateCatalogueQty(id, qty) {
if (el) el.innerText = qty;
}
function filterCatalogue(input) {
var query = input.value.toLowerCase();
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();
item.style.display = name.includes(query) ? "" : "none";
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();
}