mirror of
https://github.com/badlogic/heissepreise.git
synced 2024-06-19 23:18:45 +02:00
New cart page.
This commit is contained in:
parent
f2bf774d00
commit
d70d7bbfe3
|
@ -104,6 +104,7 @@ async function bundleJS(inputDir, outputDir, watch) {
|
|||
let buildContext = await esbuild.context({
|
||||
entryPoints: {
|
||||
carts: `${inputDir}/carts.js`,
|
||||
cart: `${inputDir}/cart.js`,
|
||||
changes: `${inputDir}/changes.js`,
|
||||
index: `${inputDir}/index.js`,
|
||||
},
|
||||
|
|
44
site/cart-old.html
Normal file
44
site/cart-old.html
Normal file
|
@ -0,0 +1,44 @@
|
|||
%%_templates/_header.html%% %%_templates/_menu.html%%
|
||||
|
||||
<div class="w-full relative px-4">
|
||||
<div id="cart">
|
||||
<h1 class="text-2xl font-bold pb-2 pt-8 text-center" id="cartname"></h1>
|
||||
<div id="noproducts" class="hidden text-center text-gray-600">Noch keine Produkte im Warenkorb</div>
|
||||
<div id="hasproducts" class="column hidden">
|
||||
<input type="button" id="save" value="Speichern" class="hidden" />
|
||||
<div class="bg-stone-200 rounded-xl p-4 max-w-4xl mx-auto md:mb-12 md:mt-6">
|
||||
<canvas class="bg-white rounded-lg py-4" id="chart"></canvas>
|
||||
<div class="filters flex items-center flex-wrap justify-center gap-2 pt-2">
|
||||
<div id="sum-container"></div>
|
||||
<div id="sumstores-container"></div>
|
||||
<div id="todayonly-container"></div>
|
||||
<div
|
||||
class="cursor-pointer inline-flex items-center gap-x-1 rounded-full bg-white border border-gray-400 px-2 py-1 text-xs font-medium text-gray-600"
|
||||
>
|
||||
<input id="start" type="date" />
|
||||
-
|
||||
<input id="end" type="date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-stone-200 rounded-xl p-4 max-w-4xl mx-auto md:mb-12 md:mt-6">
|
||||
<div>
|
||||
<input id="filter" class="search rounded-lg px-2 py-1 w-full mb-4" placeholder="Filtern... (mind. 3 Zeichen)" />
|
||||
<div class="filters flex justify-center gap-2 flex-wrap" id="filters-store"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-4 py-2 my-4 text-sm border rounded-xl md:mt-8 md:rounded-b-none md:mb-0 bg-gray-100">
|
||||
<span id="numitems"></span> <a id="json" href="" class="text-primary font-medium hover:underline">JSON</a>
|
||||
</div>
|
||||
<table id="cartitems" class="w-full"></table>
|
||||
</div>
|
||||
</div>
|
||||
<div id="search" class="w-full"></div>
|
||||
</div>
|
||||
|
||||
<script src="js/alasql.js"></script>
|
||||
<script src="js/chart.js"></script>
|
||||
<script src="utils.js"></script>
|
||||
<script src="cart-old.js"></script>
|
||||
|
||||
%%_templates/_footer.html%%
|
277
site/cart-old.js
Normal file
277
site/cart-old.js
Normal file
|
@ -0,0 +1,277 @@
|
|||
const shoppingCarts = new ShoppingCarts();
|
||||
shoppingCarts.load();
|
||||
|
||||
async function load() {
|
||||
const items = await loadItems();
|
||||
const lookup = {};
|
||||
for (item of items) {
|
||||
lookup[item.store + item.id] = item;
|
||||
}
|
||||
|
||||
let cart = null;
|
||||
const cartName = getQueryParameter("name");
|
||||
if (cartName) {
|
||||
for (c of shoppingCarts.carts) {
|
||||
if (c.name == cartName) {
|
||||
cart = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update cart pricing info
|
||||
const items = [];
|
||||
for (cartItem of cart.items) {
|
||||
const item = lookup[cartItem.store + cartItem.id];
|
||||
if (!item) items.push(cartItem);
|
||||
else items.push(item);
|
||||
}
|
||||
cart.items = items;
|
||||
shoppingCarts.save();
|
||||
}
|
||||
|
||||
const cartDesc = getQueryParameter("cart");
|
||||
if (cartDesc) {
|
||||
let tokens = cartDesc.split(";");
|
||||
cart = {
|
||||
name: tokens[0],
|
||||
items: [],
|
||||
linked: true,
|
||||
};
|
||||
for (let i = 1; i < tokens.length; i++) {
|
||||
const item = lookup[tokens[i]];
|
||||
if (item) cart.items.push(item);
|
||||
}
|
||||
let saveButton = document.querySelector("#save");
|
||||
saveButton.classList.remove("hidden");
|
||||
saveButton.addEventListener("click", () => {
|
||||
let index = shoppingCarts.carts.findIndex((c) => c.name === cart.name);
|
||||
if (index != -1) {
|
||||
if (confirm("Existierenden Warenkorb '" + cart.name + " überschreiben?")) {
|
||||
shoppingCarts.carts[index] = cart;
|
||||
}
|
||||
} else {
|
||||
shoppingCarts.carts.push(importedCart);
|
||||
}
|
||||
location.href = "/cart.html?name=" + encodeURIComponent(cart.name);
|
||||
});
|
||||
}
|
||||
|
||||
if (cart == null) {
|
||||
alert("Warenkorb '" + cartName + "' existiert nicht.");
|
||||
location.href = "carts.html";
|
||||
}
|
||||
|
||||
if (cart.name != "Momentum Eigenmarken Vergleich" && !cart.linked) showSearch(cart, items);
|
||||
|
||||
const canvasDom = document.querySelector("#chart");
|
||||
|
||||
document.querySelector("#sum-container").innerHTML = customCheckbox(`sum`, "Preissumme Gesamt", true, "gray");
|
||||
document.querySelector("#sumstores-container").innerHTML = customCheckbox(`sumstores`, "Preissumme pro Kette", true, "gray");
|
||||
document.querySelector("#todayonly-container").innerHTML = customCheckbox(`todayonly`, "Nur heutige Preise", false, "gray");
|
||||
|
||||
document.querySelector("#sum").addEventListener("change", () => updateCharts(canvasDom, filter(cart.items)));
|
||||
document.querySelector("#sumstores").addEventListener("change", () => updateCharts(canvasDom, filter(cart.items)));
|
||||
document.querySelector("#todayonly").addEventListener("change", () => updateCharts(canvasDom, filter(cart.items)));
|
||||
document.querySelector("#start").addEventListener("change", () => updateCharts(canvasDom, filter(cart.items)));
|
||||
document.querySelector("#end").addEventListener("change", () => updateCharts(canvasDom, filter(cart.items)));
|
||||
|
||||
document.querySelector("#start").value = getOldestDate(cart.items);
|
||||
document.querySelector("#end").value = currentDate();
|
||||
|
||||
const filtersStore = document.querySelector("#filters-store");
|
||||
filtersStore.innerHTML = `
|
||||
${customCheckbox("all", "<strong>Alle</strong>", true, "gray")}
|
||||
${STORE_KEYS.map((store) =>
|
||||
customCheckbox(store, stores[store].name, stores[store].name.toLowerCase().endsWith("de") ? false : true, stores[store].color)
|
||||
).join(" ")}
|
||||
`;
|
||||
|
||||
filtersStore.querySelectorAll("input").forEach((input) => {
|
||||
if (input.id == "all") return;
|
||||
input.addEventListener("change", () => showCart(cart));
|
||||
});
|
||||
filtersStore.querySelector("#all").addEventListener("change", () => {
|
||||
STORE_KEYS.forEach((store) => (filtersStore.querySelector(`#${store}`).checked = filtersStore.querySelector("#all").checked));
|
||||
showCart(cart);
|
||||
});
|
||||
document.querySelector("#filter").addEventListener("input", () => showCart(cart));
|
||||
showCart(cart);
|
||||
}
|
||||
|
||||
function filter(cartItems) {
|
||||
const query = document.querySelector("#filter").value.trim();
|
||||
const storeCheckboxes = STORE_KEYS.map((store) => document.querySelector(`#${store}`));
|
||||
const checkedStores = STORE_KEYS.filter((store, i) => storeCheckboxes[i].checked);
|
||||
let items = [];
|
||||
if (query.charAt(0) != "!") {
|
||||
for (item of cartItems) {
|
||||
if (!checkedStores.includes(item.store)) continue;
|
||||
items.push(item);
|
||||
}
|
||||
} else {
|
||||
items = cartItems;
|
||||
}
|
||||
if (query.length >= 3) items = searchItems(items, document.querySelector("#filter").value, checkedStores, false, 0, 10000, false, false);
|
||||
return items;
|
||||
}
|
||||
|
||||
function showSearch(cart, items) {
|
||||
const searchDom = document.querySelector("#search");
|
||||
searchDom.innerHTML = "";
|
||||
newSearchComponent(
|
||||
searchDom,
|
||||
items,
|
||||
null,
|
||||
null,
|
||||
(header) => {
|
||||
header.innerHTML += "<th></th>";
|
||||
return header;
|
||||
},
|
||||
(item, itemDom) => {
|
||||
const cell = dom("td", `<input type="button" class="ml-auto btn-action" value="+">`);
|
||||
cell.children[0].addEventListener("click", () => {
|
||||
cart.items.push(item);
|
||||
shoppingCarts.save();
|
||||
document.querySelector("#start").value = getOldestDate(cart.items);
|
||||
document.querySelector("#end").value = currentDate();
|
||||
showCart(cart);
|
||||
});
|
||||
cell.classList.add("order-6");
|
||||
itemDom.appendChild(cell);
|
||||
return itemDom;
|
||||
}
|
||||
);
|
||||
searchDom.querySelector("input").setAttribute("placeholder", "Produkte suchen und hinzufügen...");
|
||||
}
|
||||
|
||||
function updateCharts(canvasDom, items) {
|
||||
let startDate = document.querySelector("#start").value;
|
||||
let endDate = document.querySelector("#end").value;
|
||||
if (start > endDate) {
|
||||
let tmp = start;
|
||||
start = endDate;
|
||||
endDate = tmp;
|
||||
}
|
||||
showCharts(
|
||||
canvasDom,
|
||||
items,
|
||||
document.querySelector("#sum").checked,
|
||||
document.querySelector("#sumstores").checked,
|
||||
document.querySelector("#todayonly").checked,
|
||||
startDate,
|
||||
endDate
|
||||
);
|
||||
}
|
||||
|
||||
function showCart(cart) {
|
||||
if (cart.items.length == 0) {
|
||||
document.querySelector("#noproducts").classList.remove("hidden");
|
||||
document.querySelector("#hasproducts").classList.add("hidden");
|
||||
} else {
|
||||
document.querySelector("#noproducts").classList.add("hidden");
|
||||
document.querySelector("#hasproducts").classList.remove("hidden");
|
||||
}
|
||||
|
||||
let link = encodeURIComponent(cart.name) + ";";
|
||||
for (cartItem of cart.items) {
|
||||
link += cartItem.store + cartItem.id + ";";
|
||||
}
|
||||
|
||||
document.querySelector("#cartname").innerHTML = `
|
||||
Warenkorb '${cart.name}' <a class="text-sm text-primary block hover:underline" href="cart.html?cart=${link}">Teilen</a>
|
||||
`;
|
||||
|
||||
const canvasDom = document.querySelector("#chart");
|
||||
let items = filter(cart.items);
|
||||
if (items.length == cart.items.length) {
|
||||
document.querySelector("#numitems").innerText = `${cart.items.length} Artikel`;
|
||||
} else {
|
||||
document.querySelector("#numitems").innerText = `${items.length} / ${cart.items.length} Artikel`;
|
||||
}
|
||||
document.querySelector("#json").addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
downloadFile("items.json", JSON.stringify(items, null, 2));
|
||||
});
|
||||
updateCharts(canvasDom, items);
|
||||
|
||||
const itemTable = document.querySelector("#cartitems");
|
||||
itemTable.innerHTML = "";
|
||||
header = dom(
|
||||
"thead",
|
||||
`<tr>
|
||||
<th class="text-center">Kette</th>
|
||||
<th>Name</th>
|
||||
<th>Preis <span class="expander">+</span></th>
|
||||
<th></th>
|
||||
</tr>`
|
||||
);
|
||||
const showHideAll = header.querySelectorAll("th:nth-child(3)")[0];
|
||||
showHideAll.style["cursor"] = "pointer";
|
||||
showHideAll.showAll = true;
|
||||
showHideAll.addEventListener("click", () => {
|
||||
showHideAll.querySelector(".expander").innerText = showHideAll.querySelector(".expander").innerText == "+" ? "-" : "+";
|
||||
itemTable.querySelectorAll(".priceinfo").forEach((el) => (showHideAll.showAll ? el.classList.remove("hidden") : el.classList.add("hidden")));
|
||||
showHideAll.showAll = !showHideAll.showAll;
|
||||
});
|
||||
itemTable.append(header);
|
||||
|
||||
items.forEach((cartItem, idx) => {
|
||||
const itemDom = itemToDOM(cartItem);
|
||||
|
||||
const cell = dom(
|
||||
"td",
|
||||
`
|
||||
<label>
|
||||
<input type="checkbox" class="hidden peer">
|
||||
<span class="peer-checked:bg-blue-700 btn-action">📈</span>
|
||||
</label>
|
||||
<input type="button" class="ml-auto btn-action" value="-">
|
||||
<input type="button" class="btn-action" value="▲">
|
||||
<input type="button" class="btn-action" value="▼">
|
||||
`
|
||||
);
|
||||
cell.classList.add("action");
|
||||
|
||||
if (cartItem.chart) cell.children[0].setAttribute("checked", true);
|
||||
cell.children[0].addEventListener("change", () => {
|
||||
cartItem.chart = cell.children[0].children[0].checked;
|
||||
shoppingCarts.save();
|
||||
updateCharts(canvasDom, items);
|
||||
});
|
||||
|
||||
if (cart.name != "Momentum Eigenmarken Vergleich" && !cart.linked) {
|
||||
cell.children[1].addEventListener("click", () => {
|
||||
cart.items.splice(idx, 1);
|
||||
shoppingCarts.save();
|
||||
document.querySelector("#start").value = getOldestDate(cart.items);
|
||||
document.querySelector("#end").value = currentDate();
|
||||
showCart(cart);
|
||||
});
|
||||
|
||||
cell.children[2].addEventListener("click", () => {
|
||||
if (idx == 0) return;
|
||||
let otherItem = cart.items[idx - 1];
|
||||
cart.items[idx - 1] = cartItem;
|
||||
cart.items[idx] = otherItem;
|
||||
shoppingCarts.save();
|
||||
showCart(cart);
|
||||
});
|
||||
|
||||
cell.children[3].addEventListener("click", () => {
|
||||
if (idx == cart.items.length - 1) return;
|
||||
let otherItem = cart.items[idx + 1];
|
||||
cart.items[idx + 1] = cartItem;
|
||||
cart.items[idx] = otherItem;
|
||||
shoppingCarts.save();
|
||||
showCart(cart);
|
||||
});
|
||||
} else {
|
||||
cell.querySelectorAll("input[type='button']").forEach((button) => button.classList.add("hidden"));
|
||||
}
|
||||
|
||||
itemDom.append(cell);
|
||||
itemTable.append(itemDom);
|
||||
});
|
||||
}
|
||||
|
||||
load();
|
|
@ -1,44 +1,16 @@
|
|||
%%_templates/_header.html%% %%_templates/_menu.html%%
|
||||
|
||||
<div class="w-full relative px-4">
|
||||
<div id="cart">
|
||||
<h1 class="text-2xl font-bold pb-2 pt-8 text-center" id="cartname"></h1>
|
||||
<div id="noproducts" class="hidden text-center text-gray-600">Noch keine Produkte im Warenkorb</div>
|
||||
<div id="hasproducts" class="column hidden">
|
||||
<input type="button" id="save" value="Speichern" class="hidden" />
|
||||
<div class="bg-stone-200 rounded-xl p-4 max-w-4xl mx-auto md:mb-12 md:mt-6">
|
||||
<canvas class="bg-white rounded-lg py-4" id="chart"></canvas>
|
||||
<div class="filters flex items-center flex-wrap justify-center gap-2 pt-2">
|
||||
<div id="sum-container"></div>
|
||||
<div id="sumstores-container"></div>
|
||||
<div id="todayonly-container"></div>
|
||||
<div
|
||||
class="cursor-pointer inline-flex items-center gap-x-1 rounded-full bg-white border border-gray-400 px-2 py-1 text-xs font-medium text-gray-600"
|
||||
>
|
||||
<input id="start" type="date" />
|
||||
-
|
||||
<input id="end" type="date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-stone-200 rounded-xl p-4 max-w-4xl mx-auto md:mb-12 md:mt-6">
|
||||
<div>
|
||||
<input id="filter" class="search rounded-lg px-2 py-1 w-full mb-4" placeholder="Filtern... (mind. 3 Zeichen)" />
|
||||
<div class="filters flex justify-center gap-2 flex-wrap" id="filters-store"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-4 py-2 my-4 text-sm border rounded-xl md:mt-8 md:rounded-b-none md:mb-0 bg-gray-100">
|
||||
<span id="numitems"></span> <a id="json" href="" class="text-primary font-medium hover:underline">JSON</a>
|
||||
</div>
|
||||
<table id="cartitems" class="w-full"></table>
|
||||
</div>
|
||||
</div>
|
||||
<div id="search" class="w-full"></div>
|
||||
<div class="w-full relative px-4 flex-1">
|
||||
<cart-header x-id="cartHeader"></cart-header>
|
||||
|
||||
<div x-id="noItems" class="hidden block text-center my-3">Noch keine Produkte im Warenkorb</div>
|
||||
<items-filter x-id="cartFilter" stores nochartclear class="hidden" placeholder="Filtern..."></items-filter>
|
||||
<items-list x-id="linkedCartList" chart json nosort class="hidden"></items-list>
|
||||
<items-list x-id="cartList" chart json nosort remove updown class="hidden"></items-list>
|
||||
<items-filter x-id="productsFilter" class="hidden" stores misc placeholder="Produkt hinzufügen..."></items-filter>
|
||||
<items-list x-id="productsList" class="hidden" add></items-list>
|
||||
</div>
|
||||
|
||||
<script src="js/alasql.js"></script>
|
||||
<script src="js/chart.js"></script>
|
||||
<script src="utils.js"></script>
|
||||
<script src="cart.js"></script>
|
||||
|
||||
%%_templates/_footer.html%%
|
||||
|
|
351
site/cart.js
351
site/cart.js
|
@ -1,32 +1,87 @@
|
|||
const shoppingCarts = new ShoppingCarts();
|
||||
shoppingCarts.load();
|
||||
const { getQueryParameter } = require("./misc");
|
||||
const models = require("./model");
|
||||
const { Model } = require("./model/model");
|
||||
const { View } = require("./views/view");
|
||||
const { STORE_KEYS, stores } = require("./model/stores");
|
||||
require("./views");
|
||||
|
||||
async function load() {
|
||||
const items = await loadItems();
|
||||
const lookup = {};
|
||||
for (item of items) {
|
||||
lookup[item.store + item.id] = item;
|
||||
let carts = null;
|
||||
|
||||
class CartModel extends Model {
|
||||
constructor(cart, linked) {
|
||||
super();
|
||||
this.cart = cart;
|
||||
this.items = cart.items;
|
||||
this._filteredItems = [...this.items];
|
||||
this.linked = linked;
|
||||
}
|
||||
|
||||
get filteredItems() {
|
||||
return this._filteredItems;
|
||||
}
|
||||
|
||||
set filteredItems(newItems) {
|
||||
this._filteredItems = newItems;
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
class CartHeader extends View {
|
||||
constructor() {
|
||||
super();
|
||||
this.innerHTML = `
|
||||
<h1 class="text-2xl font-bold pb-2 pt-8 text-center">
|
||||
<span x-id="name"></span>
|
||||
</h1>
|
||||
<a x-id="share" class="hidden cursor-pointer font-bold text-sm text-primary hover:underline block text-center mt-3">Teilen</a>
|
||||
<input x-id="save" class="hidden cursor-pointer font-bold text-sm text-primary block mx-auto mt-3" type="button" value="Speichern">
|
||||
`;
|
||||
|
||||
const elements = this.elements;
|
||||
elements.save.addEventListener("click", () => {
|
||||
const cart = this.model.cart;
|
||||
let index = carts.findIndex((c) => c.name === cart.name);
|
||||
if (index != -1) {
|
||||
if (confirm("Existierenden Warenkorb '" + cart.name + " überschreiben?")) {
|
||||
carts[index] = cart;
|
||||
}
|
||||
} else {
|
||||
carts.push(cart);
|
||||
}
|
||||
// model.carts.save();
|
||||
location.href = "/cart-new.html?name=" + encodeURIComponent(cart.name);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const cart = this.model.cart;
|
||||
const elements = this.elements;
|
||||
elements.name.innerText = `Warenkorb '${cart.name}'`;
|
||||
if (this.model.linked) {
|
||||
elements.save.classList.remove("hidden");
|
||||
} else {
|
||||
elements.share.classList.remove("hidden");
|
||||
let link = encodeURIComponent(cart.name) + ";";
|
||||
for (const cartItem of cart.items) {
|
||||
link += cartItem.store + cartItem.id + ";";
|
||||
}
|
||||
elements.share.href = "cart-new.html?cart=" + link;
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define("cart-header", CartHeader);
|
||||
|
||||
function loadCart() {
|
||||
let cart = null;
|
||||
let linked = false;
|
||||
const cartName = getQueryParameter("name");
|
||||
if (cartName) {
|
||||
for (c of shoppingCarts.carts) {
|
||||
for (const c of carts) {
|
||||
if (c.name == cartName) {
|
||||
cart = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update cart pricing info
|
||||
const items = [];
|
||||
for (cartItem of cart.items) {
|
||||
const item = lookup[cartItem.store + cartItem.id];
|
||||
if (!item) items.push(cartItem);
|
||||
else items.push(item);
|
||||
}
|
||||
cart.items = items;
|
||||
shoppingCarts.save();
|
||||
}
|
||||
|
||||
const cartDesc = getQueryParameter("cart");
|
||||
|
@ -35,25 +90,12 @@ async function load() {
|
|||
cart = {
|
||||
name: tokens[0],
|
||||
items: [],
|
||||
linked: true,
|
||||
};
|
||||
for (let i = 1; i < tokens.length; i++) {
|
||||
const item = lookup[tokens[i]];
|
||||
const item = models.items.lookup[tokens[i]];
|
||||
if (item) cart.items.push(item);
|
||||
}
|
||||
let saveButton = document.querySelector("#save");
|
||||
saveButton.classList.remove("hidden");
|
||||
saveButton.addEventListener("click", () => {
|
||||
let index = shoppingCarts.carts.findIndex((c) => c.name === cart.name);
|
||||
if (index != -1) {
|
||||
if (confirm("Existierenden Warenkorb '" + cart.name + " überschreiben?")) {
|
||||
shoppingCarts.carts[index] = cart;
|
||||
}
|
||||
} else {
|
||||
shoppingCarts.carts.push(importedCart);
|
||||
}
|
||||
location.href = "/cart.html?name=" + encodeURIComponent(cart.name);
|
||||
});
|
||||
linked = true;
|
||||
}
|
||||
|
||||
if (cart == null) {
|
||||
|
@ -61,217 +103,54 @@ async function load() {
|
|||
location.href = "carts.html";
|
||||
}
|
||||
|
||||
if (cart.name != "Momentum Eigenmarken Vergleich" && !cart.linked) showSearch(cart, items);
|
||||
return new CartModel(cart, linked);
|
||||
}
|
||||
|
||||
const canvasDom = document.querySelector("#chart");
|
||||
(async () => {
|
||||
await models.load();
|
||||
carts = models.carts.carts;
|
||||
const cart = loadCart();
|
||||
|
||||
document.querySelector("#sum-container").innerHTML = customCheckbox(`sum`, "Preissumme Gesamt", true, "gray");
|
||||
document.querySelector("#sumstores-container").innerHTML = customCheckbox(`sumstores`, "Preissumme pro Kette", true, "gray");
|
||||
document.querySelector("#todayonly-container").innerHTML = customCheckbox(`todayonly`, "Nur heutige Preise", false, "gray");
|
||||
const elements = View.elements(document.body);
|
||||
const cartHeader = elements.cartHeader;
|
||||
cartHeader.model = cart;
|
||||
|
||||
document.querySelector("#sum").addEventListener("change", () => updateCharts(canvasDom, filter(cart.items)));
|
||||
document.querySelector("#sumstores").addEventListener("change", () => updateCharts(canvasDom, filter(cart.items)));
|
||||
document.querySelector("#todayonly").addEventListener("change", () => updateCharts(canvasDom, filter(cart.items)));
|
||||
document.querySelector("#start").addEventListener("change", () => updateCharts(canvasDom, filter(cart.items)));
|
||||
document.querySelector("#end").addEventListener("change", () => updateCharts(canvasDom, filter(cart.items)));
|
||||
|
||||
document.querySelector("#start").value = getOldestDate(cart.items);
|
||||
document.querySelector("#end").value = currentDate();
|
||||
|
||||
const filtersStore = document.querySelector("#filters-store");
|
||||
filtersStore.innerHTML = `
|
||||
${customCheckbox("all", "<strong>Alle</strong>", true, "gray")}
|
||||
${STORE_KEYS.map((store) =>
|
||||
customCheckbox(store, stores[store].name, stores[store].name.toLowerCase().endsWith("de") ? false : true, stores[store].color)
|
||||
).join(" ")}
|
||||
`;
|
||||
|
||||
filtersStore.querySelectorAll("input").forEach((input) => {
|
||||
if (input.id == "all") return;
|
||||
input.addEventListener("change", () => showCart(cart));
|
||||
const cartFilter = elements.cartFilter;
|
||||
const cartList = cart.linked ? elements.linkedCartList : elements.cartList;
|
||||
STORE_KEYS.forEach((store) => {
|
||||
cartFilter.elements[store].checked = true;
|
||||
});
|
||||
filtersStore.querySelector("#all").addEventListener("change", () => {
|
||||
STORE_KEYS.forEach((store) => (filtersStore.querySelector(`#${store}`).checked = filtersStore.querySelector("#all").checked));
|
||||
showCart(cart);
|
||||
});
|
||||
document.querySelector("#filter").addEventListener("input", () => showCart(cart));
|
||||
showCart(cart);
|
||||
}
|
||||
cartList.elements.numItemsLabel.innerHTML = "<strong>Artikel:</strong>";
|
||||
cartList.model = cartFilter.model = cart;
|
||||
cartList.elements.enableChart.checked = true;
|
||||
cartList.elements.chart.elements.sumStores.checked = true;
|
||||
cartList.fireChangeEvent();
|
||||
cartList.elements.chart.fireChangeEvent();
|
||||
|
||||
function filter(cartItems) {
|
||||
const query = document.querySelector("#filter").value.trim();
|
||||
const storeCheckboxes = STORE_KEYS.map((store) => document.querySelector(`#${store}`));
|
||||
const checkedStores = STORE_KEYS.filter((store, i) => storeCheckboxes[i].checked);
|
||||
let items = [];
|
||||
if (query.charAt(0) != "!") {
|
||||
for (item of cartItems) {
|
||||
if (!checkedStores.includes(item.store)) continue;
|
||||
items.push(item);
|
||||
}
|
||||
} else {
|
||||
items = cartItems;
|
||||
}
|
||||
if (query.length >= 3) items = searchItems(items, document.querySelector("#filter").value, checkedStores, false, 0, 10000, false, false);
|
||||
return items;
|
||||
}
|
||||
|
||||
function showSearch(cart, items) {
|
||||
const searchDom = document.querySelector("#search");
|
||||
searchDom.innerHTML = "";
|
||||
newSearchComponent(
|
||||
searchDom,
|
||||
items,
|
||||
null,
|
||||
null,
|
||||
(header) => {
|
||||
header.innerHTML += "<th></th>";
|
||||
return header;
|
||||
},
|
||||
(item, itemDom) => {
|
||||
const cell = dom("td", `<input type="button" class="ml-auto btn-action" value="+">`);
|
||||
cell.children[0].addEventListener("click", () => {
|
||||
cart.items.push(item);
|
||||
shoppingCarts.save();
|
||||
document.querySelector("#start").value = getOldestDate(cart.items);
|
||||
document.querySelector("#end").value = currentDate();
|
||||
showCart(cart);
|
||||
});
|
||||
cell.classList.add("order-6");
|
||||
itemDom.appendChild(cell);
|
||||
return itemDom;
|
||||
}
|
||||
);
|
||||
searchDom.querySelector("input").setAttribute("placeholder", "Produkte suchen und hinzufügen...");
|
||||
}
|
||||
|
||||
function updateCharts(canvasDom, items) {
|
||||
let startDate = document.querySelector("#start").value;
|
||||
let endDate = document.querySelector("#end").value;
|
||||
if (start > endDate) {
|
||||
let tmp = start;
|
||||
start = endDate;
|
||||
endDate = tmp;
|
||||
}
|
||||
showCharts(
|
||||
canvasDom,
|
||||
items,
|
||||
document.querySelector("#sum").checked,
|
||||
document.querySelector("#sumstores").checked,
|
||||
document.querySelector("#todayonly").checked,
|
||||
startDate,
|
||||
endDate
|
||||
);
|
||||
}
|
||||
|
||||
function showCart(cart) {
|
||||
if (cart.items.length == 0) {
|
||||
document.querySelector("#noproducts").classList.remove("hidden");
|
||||
document.querySelector("#hasproducts").classList.add("hidden");
|
||||
elements.noItems.classList.remove("hidden");
|
||||
} else {
|
||||
document.querySelector("#noproducts").classList.add("hidden");
|
||||
document.querySelector("#hasproducts").classList.remove("hidden");
|
||||
cartFilter.classList.remove("hidden");
|
||||
cartList.classList.remove("hidden");
|
||||
}
|
||||
|
||||
let link = encodeURIComponent(cart.name) + ";";
|
||||
for (cartItem of cart.items) {
|
||||
link += cartItem.store + cartItem.id + ";";
|
||||
cartList.removeCallback = (item) => models.carts.save();
|
||||
cartList.upCallback = (item) => models.carts.save();
|
||||
cartList.downCallback = (item) => models.carts.save();
|
||||
|
||||
const productsFilter = elements.productsFilter;
|
||||
const productsList = elements.productsList;
|
||||
productsList.model = productsFilter.model = models.items;
|
||||
if (!cart.linked) {
|
||||
productsFilter.classList.remove("hidden");
|
||||
productsList.classList.remove("hidden");
|
||||
}
|
||||
|
||||
document.querySelector("#cartname").innerHTML = `
|
||||
Warenkorb '${cart.name}' <a class="text-sm text-primary block hover:underline" href="cart.html?cart=${link}">Teilen</a>
|
||||
`;
|
||||
|
||||
const canvasDom = document.querySelector("#chart");
|
||||
let items = filter(cart.items);
|
||||
if (items.length == cart.items.length) {
|
||||
document.querySelector("#numitems").innerText = `${cart.items.length} Artikel`;
|
||||
} else {
|
||||
document.querySelector("#numitems").innerText = `${items.length} / ${cart.items.length} Artikel`;
|
||||
}
|
||||
document.querySelector("#json").addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
downloadFile("items.json", JSON.stringify(items, null, 2));
|
||||
});
|
||||
updateCharts(canvasDom, items);
|
||||
|
||||
const itemTable = document.querySelector("#cartitems");
|
||||
itemTable.innerHTML = "";
|
||||
header = dom(
|
||||
"thead",
|
||||
`<tr>
|
||||
<th class="text-center">Kette</th>
|
||||
<th>Name</th>
|
||||
<th>Preis <span class="expander">+</span></th>
|
||||
<th></th>
|
||||
</tr>`
|
||||
);
|
||||
const showHideAll = header.querySelectorAll("th:nth-child(3)")[0];
|
||||
showHideAll.style["cursor"] = "pointer";
|
||||
showHideAll.showAll = true;
|
||||
showHideAll.addEventListener("click", () => {
|
||||
showHideAll.querySelector(".expander").innerText = showHideAll.querySelector(".expander").innerText == "+" ? "-" : "+";
|
||||
itemTable.querySelectorAll(".priceinfo").forEach((el) => (showHideAll.showAll ? el.classList.remove("hidden") : el.classList.add("hidden")));
|
||||
showHideAll.showAll = !showHideAll.showAll;
|
||||
});
|
||||
itemTable.append(header);
|
||||
|
||||
items.forEach((cartItem, idx) => {
|
||||
const itemDom = itemToDOM(cartItem);
|
||||
|
||||
const cell = dom(
|
||||
"td",
|
||||
`
|
||||
<label>
|
||||
<input type="checkbox" class="hidden peer">
|
||||
<span class="peer-checked:bg-blue-700 btn-action">📈</span>
|
||||
</label>
|
||||
<input type="button" class="ml-auto btn-action" value="-">
|
||||
<input type="button" class="btn-action" value="▲">
|
||||
<input type="button" class="btn-action" value="▼">
|
||||
`
|
||||
);
|
||||
cell.classList.add("action");
|
||||
|
||||
if (cartItem.chart) cell.children[0].setAttribute("checked", true);
|
||||
cell.children[0].addEventListener("change", () => {
|
||||
cartItem.chart = cell.children[0].children[0].checked;
|
||||
shoppingCarts.save();
|
||||
updateCharts(canvasDom, items);
|
||||
});
|
||||
|
||||
if (cart.name != "Momentum Eigenmarken Vergleich" && !cart.linked) {
|
||||
cell.children[1].addEventListener("click", () => {
|
||||
cart.items.splice(idx, 1);
|
||||
shoppingCarts.save();
|
||||
document.querySelector("#start").value = getOldestDate(cart.items);
|
||||
document.querySelector("#end").value = currentDate();
|
||||
showCart(cart);
|
||||
});
|
||||
|
||||
cell.children[2].addEventListener("click", () => {
|
||||
if (idx == 0) return;
|
||||
let otherItem = cart.items[idx - 1];
|
||||
cart.items[idx - 1] = cartItem;
|
||||
cart.items[idx] = otherItem;
|
||||
shoppingCarts.save();
|
||||
showCart(cart);
|
||||
});
|
||||
|
||||
cell.children[3].addEventListener("click", () => {
|
||||
if (idx == cart.items.length - 1) return;
|
||||
let otherItem = cart.items[idx + 1];
|
||||
cart.items[idx + 1] = cartItem;
|
||||
cart.items[idx] = otherItem;
|
||||
shoppingCarts.save();
|
||||
showCart(cart);
|
||||
});
|
||||
} else {
|
||||
cell.querySelectorAll("input[type='button']").forEach((button) => button.classList.add("hidden"));
|
||||
}
|
||||
|
||||
itemDom.append(cell);
|
||||
itemTable.append(itemDom);
|
||||
});
|
||||
}
|
||||
|
||||
load();
|
||||
productsList.addCallback = (item) => {
|
||||
cart.items.push(item);
|
||||
models.carts.save();
|
||||
cartFilter.filter();
|
||||
cartFilter.classList.remove("hidden");
|
||||
cartList.classList.remove("hidden");
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -17,7 +17,8 @@ require("./views");
|
|||
.filter((item) => item.chart)
|
||||
.map((item) => item.store + item.id)
|
||||
.join(";");
|
||||
history.pushState({}, null, location.pathname + "?f=" + filterState + "&l=" + listState + "&c=" + chartState + "&d=" + chartedItems);
|
||||
|
||||
history.replaceState({}, null, location.pathname + "?f=" + filterState + "&l=" + listState + "&c=" + chartState + "&d=" + chartedItems);
|
||||
};
|
||||
|
||||
itemsFilter.addEventListener("x-change", stateToUrl);
|
||||
|
|
|
@ -2,3 +2,4 @@ require("./custom-checkbox");
|
|||
require("./carts-list");
|
||||
require("./items-filter");
|
||||
require("./items-list");
|
||||
require("./view");
|
||||
|
|
|
@ -11,6 +11,7 @@ class ItemsFilter extends View {
|
|||
this._filterByPriceDirection = getBooleanAttribute(this, "pricedirection");
|
||||
this._filterByStores = getBooleanAttribute(this, "stores");
|
||||
this._filterByMisc = getBooleanAttribute(this, "misc");
|
||||
this._noChartClear = getBooleanAttribute(this, "nochartclear");
|
||||
|
||||
const hidePriceChanges = this._filterByPriceChanges ? "" : "hidden";
|
||||
const hidePriceDirection = this._filterByPriceDirection ? "" : "hidden";
|
||||
|
@ -76,8 +77,8 @@ class ItemsFilter extends View {
|
|||
elements.stores.classList.add("hidden");
|
||||
elements.misc.classList.add("hidden");
|
||||
} else {
|
||||
elements.stores.classList.remove("hidden");
|
||||
elements.misc.classList.remove("hidden");
|
||||
if (!hideStores) elements.stores.classList.remove("hidden");
|
||||
if (!hideMisc) elements.misc.classList.remove("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -193,7 +194,7 @@ class ItemsFilter extends View {
|
|||
filteredItems = queryItems(query, filteredItems, elements.exact.checked);
|
||||
}
|
||||
|
||||
if (this.model.lastQuery != query) {
|
||||
if (this.model.lastQuery != query && !this._noChartClear) {
|
||||
filteredItems.forEach((item) => (item.chart = false));
|
||||
}
|
||||
this.model.lastQuery = query;
|
||||
|
|
|
@ -11,21 +11,25 @@ class ItemsList extends View {
|
|||
this._json = getBooleanAttribute(this, "json");
|
||||
this._chart = getBooleanAttribute(this, "chart");
|
||||
this._remove = getBooleanAttribute(this, "remove");
|
||||
this._remove = getBooleanAttribute(this, "add");
|
||||
this._add = getBooleanAttribute(this, "add");
|
||||
this._updown = getBooleanAttribute(this, "updown");
|
||||
this._noSort = getBooleanAttribute(this, "nosort");
|
||||
const hideSort = this._noSort ? "hidden" : "";
|
||||
|
||||
this.innerHTML = /*html*/ `
|
||||
<div x-id="options" class="hidden flex flex-col md:flex-row gap-4 px-4 py-2 my-4 justify-between items-center text-sm border rounded-xl md:mt-8 md:rounded-b-none md:mb-0 bg-gray-100 ">
|
||||
<div>
|
||||
<div class="flex flex-col md:flex-row gap-2 items-center">
|
||||
<span x-id="numItems"></span>
|
||||
<span x-id="numItemsLabel">Resultate</span><span x-id="numItems"></span>
|
||||
<span>
|
||||
<a x-id="json" class="hidden text-primary font-medium hover:underline" href="">JSON</a>
|
||||
</span>
|
||||
<custom-checkbox x-id="enableChart" x-change x-state label="Diagramm" class="${this._chart}"></custom-checkbox>
|
||||
<custom-checkbox x-id="enableChart" x-change x-state label="Diagramm" class="${
|
||||
this._chart ? "" : "hidden"
|
||||
}"></custom-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<label>
|
||||
<label class="${hideSort}">
|
||||
Sortieren
|
||||
<select x-id="sort" x-change x-state>
|
||||
<option value="price-asc">Preis aufsteigend</option>
|
||||
|
@ -180,7 +184,7 @@ class ItemsList extends View {
|
|||
<input x-id="chartCheckbox" type="checkbox" class="hidden peer">
|
||||
<span class="peer-checked:bg-blue-700 btn-action">📈</span>
|
||||
</label>
|
||||
<input x-id="add" type="button" class="${this._remove ? "" : "hidden"} btn-action" value="+">
|
||||
<input x-id="add" type="button" class="${this._add ? "" : "hidden"} btn-action" value="+">
|
||||
<input x-id="remove" type="button" class="${this._remove ? "" : "hidden"} btn-action" value="-">
|
||||
<input x-id="up" type="button" class="${this._updown ? "" : "hidden"} btn-action" value="▲">
|
||||
<input x-id="down" type="button" class="${this._updown ? "" : "hidden"} btn-action" value="▼">
|
||||
|
@ -277,6 +281,53 @@ class ItemsList extends View {
|
|||
this.fireChangeEvent();
|
||||
});
|
||||
}
|
||||
|
||||
if (this.model.items.length != this.model.filteredItems.length) {
|
||||
elements.up.classList.add("hidden");
|
||||
elements.down.classList.add("hidden");
|
||||
}
|
||||
|
||||
elements.add.addEventListener("click", () => {
|
||||
if (this._addCallback) this._addCallback(item);
|
||||
});
|
||||
|
||||
elements.remove.addEventListener("click", () => {
|
||||
let index = this.model.items.indexOf(item);
|
||||
if (index >= 0) this.model.items.splice(index, 1);
|
||||
index = this.model.filteredItems.indexOf(item);
|
||||
if (index >= 0) this.model.filteredItems.splice(index, 1);
|
||||
itemDom.remove();
|
||||
this.elements.chart.render();
|
||||
|
||||
if (this._removeCallback) this._removeCallback(item);
|
||||
});
|
||||
|
||||
elements.up.addEventListener("click", () => {
|
||||
const index = itemDom.rowIndex - 1;
|
||||
if (index == 0) return;
|
||||
let otherItem = this.model.items[index - 1];
|
||||
this.model.items[index - 1] = item;
|
||||
this.model.items[index] = otherItem;
|
||||
|
||||
let rowBefore = this.elements.tableBody.rows[index - 1];
|
||||
this.elements.tableBody.insertBefore(itemDom, rowBefore);
|
||||
|
||||
if (this._upCallback) this._upCallback(item);
|
||||
});
|
||||
|
||||
elements.down.addEventListener("click", () => {
|
||||
const index = itemDom.rowIndex - 1;
|
||||
if (index == this.model.items.length - 1) return;
|
||||
let otherItem = this.model.items[index + 1];
|
||||
this.model.items[index + 1] = item;
|
||||
this.model.items[index] = otherItem;
|
||||
|
||||
let rowAfter = this.elements.tableBody.rows[index + 1];
|
||||
this.elements.tableBody.insertBefore(rowAfter, itemDom);
|
||||
|
||||
if (this._downCallback) this._downCallback(item);
|
||||
});
|
||||
|
||||
if (this._showAllPriceHistories) elements.priceHistory.classList.remove("hidden");
|
||||
return itemDom;
|
||||
}
|
||||
|
@ -295,8 +346,10 @@ class ItemsList extends View {
|
|||
if (this.model.lastQuery && this.model.lastQuery.charAt(0) == "!") {
|
||||
elements.sort.parentElement.classList.add("hidden");
|
||||
} else {
|
||||
elements.sort.parentElement.classList.remove("hidden");
|
||||
items = this.sort(items);
|
||||
if (!this._noSort) {
|
||||
elements.sort.parentElement.classList.remove("hidden");
|
||||
items = this.sort(items);
|
||||
}
|
||||
}
|
||||
if (items.length == 0) {
|
||||
elements.chart.classList.add("hidden");
|
||||
|
@ -307,21 +360,20 @@ class ItemsList extends View {
|
|||
elements.options.classList.remove("hidden");
|
||||
elements.itemsTable.classList.remove("hidden");
|
||||
}
|
||||
elements.numItems.innerHTML =
|
||||
"<strong>Resultate:</strong> " + items.length + (this.model.totalItems > items.length ? " / " + this.model.totalItems : "");
|
||||
elements.numItems.innerHTML = items.length + (this.model.totalItems > items.length ? " / " + this.model.totalItems : "");
|
||||
const tableBody = elements.tableBody;
|
||||
tableBody.innerHTML = "";
|
||||
|
||||
let i = 0;
|
||||
const batches = [];
|
||||
let batch = [];
|
||||
for (const item of items) {
|
||||
items.forEach((item, index) => {
|
||||
if (batch.length == 100) {
|
||||
batches.push(batch);
|
||||
batch = [];
|
||||
}
|
||||
batch.push(item);
|
||||
}
|
||||
});
|
||||
if (batch.length > 0) batches.push(batch);
|
||||
|
||||
const renderBatch = () => {
|
||||
|
@ -339,6 +391,22 @@ class ItemsList extends View {
|
|||
|
||||
log(`ItemsList - rendering ${items.length} items took ${deltaTime(start).toFixed(4)} secs`);
|
||||
}
|
||||
|
||||
set addCallback(callback) {
|
||||
this._addCallback = callback;
|
||||
}
|
||||
|
||||
set removeCallback(callback) {
|
||||
this._removeCallback = callback;
|
||||
}
|
||||
|
||||
set upCallback(callback) {
|
||||
this._upCallback = callback;
|
||||
}
|
||||
|
||||
set downCallback(callback) {
|
||||
this._downCallback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("items-list", ItemsList);
|
||||
|
|
|
@ -41,7 +41,7 @@ class View extends HTMLElement {
|
|||
const result = {};
|
||||
elements.forEach((element) => {
|
||||
if (result[element.getAttribute("x-id")]) {
|
||||
console.log(`Duplicate element x-id ${element.getAttribute("x-id")} in ${view.localName}`);
|
||||
log(`View - Duplicate element x-id ${element.getAttribute("x-id")} in ${view.localName}`);
|
||||
}
|
||||
result[element.getAttribute("x-id")] = element;
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user