mirror of
https://github.com/badlogic/heissepreise.git
synced 2024-07-02 21:35:49 +02:00
303d25ccb5
Billa maps directly to the canonical categories. Spar uses a mapping file stores/spar-categories.json. Each store has a generateCategoryMapping() function which is called once in analysis.js:updateData() and analysis.js:replay(). The function is responsible for * Fetching the latest categories * Merging them with already mapped categories * Report new categories that haven't been mapped yet * Report categories that have been mapped but are no longer part of the latest set of categories * Save the merged mappings to disk This schema might not work for all stores, in which case updateData() and replay() will use a knn approach to figure out the category for an item. See #81
213 lines
7.0 KiB
JavaScript
213 lines
7.0 KiB
JavaScript
const { getQueryParameter, today } = require("./js/misc");
|
|
const models = require("./model");
|
|
const { Model } = require("./model/model");
|
|
const { View } = require("./views/view");
|
|
const { STORE_KEYS, stores } = require("./model/stores");
|
|
require("./views");
|
|
|
|
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) {
|
|
let newName = cart.name;
|
|
while (true) {
|
|
newName = prompt(
|
|
"Warenkorb '" + cart.name + " existiert bereits. Bitte einen anderen Namen für den zu speichernden Warenkorb eingeben",
|
|
cart.name + today()
|
|
);
|
|
if (!newName || newName.trim().length == 0) return;
|
|
newName = newName.trim();
|
|
if (newName != cart.name) {
|
|
cart.name = newName;
|
|
carts.push(cart);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
carts.push(cart);
|
|
}
|
|
models.carts.save();
|
|
location.href = location.pathname + "?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.html?cart=" + link + (this.stateToUrl ? this.stateToUrl() : "");
|
|
}
|
|
}
|
|
}
|
|
customElements.define("cart-header", CartHeader);
|
|
|
|
function loadCart() {
|
|
let cart = null;
|
|
let linked = false;
|
|
const cartName = getQueryParameter("name");
|
|
if (cartName) {
|
|
for (const c of carts) {
|
|
if (c.name == cartName) {
|
|
cart = c;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const cartDesc = getQueryParameter("cart");
|
|
if (cartDesc) {
|
|
let tokens = cartDesc.split(";");
|
|
cart = {
|
|
name: tokens[0],
|
|
items: [],
|
|
};
|
|
for (let i = 1; i < tokens.length; i++) {
|
|
const item = models.items.lookup[tokens[i]];
|
|
if (item) cart.items.push(item);
|
|
}
|
|
linked = true;
|
|
}
|
|
|
|
if (cart == null) {
|
|
alert("Warenkorb '" + cartName + "' existiert nicht.");
|
|
location.href = "carts.html";
|
|
}
|
|
|
|
return new CartModel(cart, linked);
|
|
}
|
|
|
|
(async () => {
|
|
await models.load();
|
|
carts = models.carts.carts;
|
|
const cart = loadCart();
|
|
|
|
const elements = View.elements(document.body);
|
|
const cartHeader = elements.cartHeader;
|
|
cartHeader.model = cart;
|
|
|
|
const cartFilter = elements.cartFilter;
|
|
const cartList = cart.linked ? elements.linkedCartList : elements.cartList;
|
|
STORE_KEYS.forEach((store) => {
|
|
cartFilter.elements[store].checked = true;
|
|
});
|
|
cartList.elements.numItemsLabel.innerHTML = "<strong>Artikel:</strong>";
|
|
cartList.elements.enableChart.checked = true;
|
|
cartList.elements.chart.elements.sumStores.checked = true;
|
|
|
|
if (cart.items.length == 0) {
|
|
elements.noItems.classList.remove("hidden");
|
|
} else {
|
|
cartFilter.classList.remove("hidden");
|
|
cartList.classList.remove("hidden");
|
|
}
|
|
|
|
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;
|
|
if (!cart.linked) {
|
|
productsFilter.classList.remove("hidden");
|
|
productsList.classList.remove("hidden");
|
|
}
|
|
|
|
productsList.addCallback = (item) => {
|
|
cart.items.push(item);
|
|
models.carts.save();
|
|
cartFilter.filter();
|
|
cartFilter.classList.remove("hidden");
|
|
cartList.classList.remove("hidden");
|
|
};
|
|
|
|
const itemsFilter = cartFilter;
|
|
const itemsList = cartList;
|
|
const itemsChart = cartList.querySelector("items-chart");
|
|
itemsList.elements.sort.value = "store-and-name";
|
|
let baseUrl = location.href.split("&")[0];
|
|
|
|
const stateToUrl = () => {
|
|
const filterState = itemsFilter.shareableState;
|
|
const listState = itemsList.shareableState;
|
|
const chartState = itemsChart.shareableState;
|
|
const chartedItems = cart.filteredItems
|
|
.filter((item) => item.chart)
|
|
.map((item) => item.store + item.id)
|
|
.join(";");
|
|
return baseUrl + "&f=" + filterState + "&l=" + listState + "&c=" + chartState + "&d=" + chartedItems;
|
|
};
|
|
cartHeader.stateToUrl = stateToUrl;
|
|
itemsFilter.addEventListener("x-change", () => {
|
|
const url = stateToUrl();
|
|
history.pushState({}, null, url);
|
|
cartHeader.render();
|
|
});
|
|
itemsList.addEventListener("x-change", () => {
|
|
const url = stateToUrl();
|
|
history.pushState({}, null, url);
|
|
cartHeader.render();
|
|
});
|
|
|
|
const f = getQueryParameter("f");
|
|
const l = getQueryParameter("l");
|
|
const c = getQueryParameter("c");
|
|
const d = getQueryParameter("d");
|
|
|
|
if (f) itemsFilter.shareableState = f;
|
|
if (l) itemsList.shareableState = l;
|
|
if (c) itemsChart.shareableState = c;
|
|
if (d) {
|
|
cart.items.lookup = {};
|
|
for (const item of cart.items) cart.items.lookup[item.store + item.id] = item;
|
|
for (const id of d.split(";")) {
|
|
cart.items.lookup[id].chart = true;
|
|
}
|
|
}
|
|
cartList.model = cartFilter.model = cart;
|
|
productsList.model = productsFilter.model = models.items;
|
|
if (c || d) itemsChart.render();
|
|
cartFilter.filter();
|
|
document.querySelector('[x-id="loader"]').classList.add("hidden");
|
|
})();
|