Improve responsiveness of input box via timeout delay, add date range to chart, add sum and sum by store charting to main page.

This commit is contained in:
Mario Zechner 2023-06-01 17:40:11 +02:00
parent 08103a4c95
commit 6449ec971a
5 changed files with 118 additions and 41 deletions

View File

@ -26,6 +26,10 @@
<label><input type="checkbox" id="sumstores" checked> Preissumme pro Kette</label>
<label><input type="checkbox" id="todayonly"> Nur heutige Preise</label>
</div>
<div>
<label>Von <input id="start" type="date"></label>
<label>Bis <input id="end"type="date"></label>
</div>
<hr>
<input id="filter" type="text" style="max-width: 800px; width: 100%; margin-bottom: 1em;" placeholder="Filtern...">
<div class="filters" id="filters-store">

View File

@ -67,6 +67,10 @@ async function load() {
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 = STORE_KEYS.map(store => `<label><input id="${store}" type="checkbox" checked="true">${stores[store].name}</label>`).join(" ");
@ -103,6 +107,8 @@ function showSearch(cart, items) {
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);
});
itemDom.appendChild(cell);
@ -111,7 +117,22 @@ function showSearch(cart, items) {
}
function updateCharts(canvasDom, items) {
showCharts(canvasDom, items, document.querySelector("#sum").checked, document.querySelector("#sumstores").checked, document.querySelector("#todayonly").checked);
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) {
@ -156,6 +177,8 @@ function showCart(cart) {
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)
});

View File

@ -17,7 +17,18 @@
<a href="changes.html">Tagespreisänderungen</a>
<a href="carts.html">Warenkörbe</a>
</div>
<canvas id="chart" style="display: none;"></canvas>
<div id="chart" class="column hide" style="width: 80%">
<canvas></canvas>
<div class="filters" style="margin-top: 1em;">
<label><input type="checkbox" id="sum"> Preissumme Gesamt</label>
<label><input type="checkbox" id="sumstores"> Preissumme pro Kette</label>
<label><input type="checkbox" id="todayonly"> Nur heutige Preise</label>
</div>
<div>
<label>Von <input id="start" type="date"></label>
<label>Bis <input id="end"type="date"></label>
</div>
</div>
<div id="search" class="column">
</div>
</div>

View File

@ -1,10 +1,41 @@
function updateCharts(canvasDom, items) {
const now =performance.now();
const sum = document.querySelector("#sum").checked;
const sumStores = document.querySelector("#sumstores").checked;
const todayOnly = document.querySelector("#todayonly").checked;
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, sum, sumStores, todayOnly, startDate, endDate);
console.log("Updating charts took: " + (performance.now() - now) / 1000 + " seconds");
}
async function load() {
const items = await loadItems();
const chartDom = document.querySelector("#chart");
const canvasDom = chartDom.querySelector("canvas");
let lastHits = null;
document.querySelector("#sum").addEventListener("change", () => updateCharts(canvasDom, lastHits));
document.querySelector("#sumstores").addEventListener("change", () => updateCharts(canvasDom, lastHits));
document.querySelector("#todayonly").addEventListener("change", () => updateCharts(canvasDom, lastHits));
document.querySelector("#start").addEventListener("change", () => updateCharts(canvasDom, lastHits));
document.querySelector("#end").addEventListener("change", () => updateCharts(canvasDom, lastHits));
document.querySelector("#start").value = getOldestDate(items);
document.querySelector("#end").value = currentDate();
newSearchComponent(document.querySelector("#search"), items,
(hits) => {
items.forEach(item => item.chart = false);
showChart(chartDom, []);
if (hits.length > 0) {
chartDom.classList.remove("hide");
} else {
chartDom.classList.add("hide");
}
updateCharts(canvasDom, hits);
lastHits = hits;
return hits;
},
null,
@ -19,28 +50,16 @@ async function load() {
})
return header;
}, (item, itemDom, items, setQuery) => {
const chartCheckbox = dom("input");
const checked = (getQueryParameter("c") ?? []).includes(`${item.store}:${item.id}`);
chartCheckbox.setAttribute("type", "checkbox");
chartCheckbox.checked = checked;
item.chart = checked;
chartCheckbox.setAttribute("data-id", `${item.store}:${item.id}`);
const cell = dom("td", "");
cell.appendChild(chartCheckbox);
const checked = item.chart = (getQueryParameter("c") ?? []).includes(`${item.store}:${item.id}`);
const dataId = item.store + ":" + item.id;
const cell = dom("td", `<input type="checkbox" ${checked ? "checked" : ""} data-id="${dataId}">`);
itemDom.appendChild(cell);
const handleClick = (eventShouldSetQuery = false) =>{
item.chart = chartCheckbox.checked;
const data = [];
items.forEach(i => { if (i.chart) data.push(i) });
if (data.length == 0) {
chartDom.style.display = "none";
} else {
chartDom.style.display = "block";
showChart(chartDom, data);
}
item.chart = cell.children[0].checked;
updateCharts(canvasDom, lastHits)
!!eventShouldSetQuery && setQuery();
}
chartCheckbox.addEventListener("click", handleClick);
cell.children[0].addEventListener("click", handleClick);
checked && handleClick();
return itemDom;
});
@ -49,7 +68,7 @@ async function load() {
document.querySelector("input").value = query;
const inputEvent = new Event('input', {
bubbles: true,
cancelable: true
cancelable: false
});
document.querySelector("input").dispatchEvent(inputEvent);
}

View File

@ -486,7 +486,7 @@ function newSearchComponent(parentElement, items, searched, filter, headerModifi
now = performance.now();
let num = 0;
let limit = isMobile() ? 500 : 2000;
let limit = 500; // isMobile() ? 500 : 2000;
hits.every(hit => {
let itemDom = itemToDOM(hit);
if (itemDomModifier) itemDom = itemDomModifier(hit, itemDom, hits, setQuery);
@ -499,19 +499,23 @@ function newSearchComponent(parentElement, items, searched, filter, headerModifi
lastHits = hits;
}
let timeoutId;
searchInput.addEventListener("input", (event) => {
const query = searchInput.value.trim();
if (query == 0) {
minPrice.value = 0;
maxPrice.value = 100;
}
if (query?.charAt(0) == "!") {
parentElement.querySelectorAll(".filters").forEach(f => f.style.display = "none");
} else {
parentElement.querySelectorAll(".filters").forEach(f => f.style.display = "block");
}
setQuery();
search(searchInput.value);
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
const query = searchInput.value.trim();
if (query == 0) {
minPrice.value = 0;
maxPrice.value = 100;
}
if (query?.charAt(0) == "!") {
parentElement.querySelectorAll(".filters").forEach(f => f.style.display = "none");
} else {
parentElement.querySelectorAll(".filters").forEach(f => f.style.display = "block");
}
setQuery();
search(searchInput.value);
}, 50);
});
budgetBrands.addEventListener("change", () => search(searchInput.value));
bio.addEventListener("change", () => search(searchInput.value));
@ -587,13 +591,21 @@ function showChart(canvasDom, items, chartType) {
document.documentElement.scrollTop = scrollTop;
}
function showCharts(canvasDom, items, sum, sumStores, todayOnly) {
function getOldestDate(items) {
let oldestDate = "9999-01-01";
for (item of items) {
if (oldestDate > item.dateOldest) oldestDate = item.dateOldest;
}
return oldestDate;
}
function showCharts(canvasDom, items, sum, sumStores, todayOnly, startDate, endDate) {
let itemsToShow = [];
if (sum && items.length > 0) {
itemsToShow.push({
name: "Preissumme Warenkorb",
priceHistory: calculateOverallPriceChanges(items, todayOnly)
priceHistory: calculateOverallPriceChanges(items, todayOnly, startDate, endDate)
});
}
@ -603,20 +615,27 @@ function showCharts(canvasDom, items, sum, sumStores, todayOnly) {
if (storeItems.length > 0) {
itemsToShow.push({
name: "Preissumme " + store,
priceHistory: calculateOverallPriceChanges(storeItems, todayOnly)
priceHistory: calculateOverallPriceChanges(storeItems, todayOnly, startDate, endDate)
});
}
});
}
items.forEach((item) => {
if (item.chart) itemsToShow.push({ name: item.store + " " + item.name, priceHistory: todayOnly ? [{date: currentDate(), price: item.price}] : item.priceHistory});
if (item.chart) {
itemsToShow.push({
name: item.store + " " + item.name,
priceHistory: todayOnly ?
[{ date: currentDate(), price: item.price }] :
item.priceHistory.filter(price => price.date >= startDate && price.date <= endDate)
});
}
});
showChart(canvasDom, itemsToShow, todayOnly ? "bar" : "line");
}
function calculateOverallPriceChanges(items, todayOnly) {
function calculateOverallPriceChanges(items, todayOnly, startDate, endDate) {
if (items.length == 0) return { dates: [], changes: [] };
if (todayOnly) {
@ -626,8 +645,9 @@ function calculateOverallPriceChanges(items, todayOnly) {
}
const allDates = items.flatMap(product => product.priceHistory.map(item => item.date));
const uniqueDates = [...new Set(allDates)];
let uniqueDates = [...new Set(allDates)];
uniqueDates.sort();
uniqueDates = uniqueDates.filter(date => date >= startDate && date <= endDate);
const allPrices = items.map(product => {
let price = null;