mirror of
https://github.com/badlogic/heissepreise.git
synced 2024-06-16 05:34:21 +02:00
Use new changes.js
This commit is contained in:
parent
833dd632fa
commit
d7426bc395
|
@ -104,7 +104,7 @@ async function bundleJS(inputDir, outputDir, watch) {
|
|||
let buildContext = await esbuild.context({
|
||||
entryPoints: {
|
||||
carts: `${inputDir}/carts.js`,
|
||||
"changes-new": `${inputDir}/changes-new.js`,
|
||||
changes: `${inputDir}/changes.js`,
|
||||
index: `${inputDir}/index.js`,
|
||||
},
|
||||
bundle: true,
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
%%_templates/_header.html%% %%_templates/_menu.html%%
|
||||
|
||||
<div class="w-full relative px-4 flex-1">
|
||||
<h1 class="text-2xl font-bold pb-2 pt-8 text-center">Preisänderungen</h1>
|
||||
<items-filter x-id="items-filter" pricechanges pricedirection stores misc placeholder="Filtern..."></items-filter>
|
||||
<items-list chart></items-list>
|
||||
</div>
|
||||
|
||||
<script src="changes-new.js"></script>
|
||||
|
||||
%%_templates/_footer.html%%
|
|
@ -1,11 +0,0 @@
|
|||
const model = require("./model");
|
||||
require("./views");
|
||||
|
||||
(async () => {
|
||||
await model.load();
|
||||
const itemsFilter = document.querySelector("items-filter");
|
||||
const itemsList = document.querySelector("items-list");
|
||||
itemsList.elements.sort.value = "chain-and-name";
|
||||
itemsFilter.model = itemsList.model = model.items;
|
||||
itemsFilter.filter();
|
||||
})();
|
25
site/changes-old.html
Normal file
25
site/changes-old.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
%%_templates/_header.html%% %%_templates/_menu.html%%
|
||||
|
||||
<div class="w-full relative px-4 flex-1">
|
||||
<h1 class="text-2xl font-bold pb-2 pt-8 text-center">Preisänderungen</h1>
|
||||
<div class="bg-stone-200 rounded-xl p-4 max-w-4xl mx-auto md:mb-12 md:mt-6">
|
||||
<div class="filters mb-4 text-sm flex justify-center gap-4">
|
||||
<label>
|
||||
<input type="radio" id="date" name="type" value="date" checked /> Datum
|
||||
<select id="dates" class="bg-white rounded-full"></select>
|
||||
</label>
|
||||
<label><input type="radio" id="cheaper" name="type" value="cheaper" /> Billiger seit letzter Änderung</label>
|
||||
</div>
|
||||
<input id="filter" class="search rounded-lg px-2 py-1 w-full mb-4" type="text" placeholder="Filtern... (mind. 3 Zeichen)" />
|
||||
<div class="filters flex justify-center gap-2 flex-wrap" id="filters-store"></div>
|
||||
<div class="filters flex justify-center gap-2 mt-4" id="filters-changes"></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" id="numresults"></div>
|
||||
<table id="result" class="hidden w-full"></table>
|
||||
</div>
|
||||
|
||||
<script src="js/alasql.js"></script>
|
||||
<script src="utils.js"></script>
|
||||
<script src="changes-old.js"></script>
|
||||
|
||||
%%_templates/_footer.html%%
|
164
site/changes-old.js
Normal file
164
site/changes-old.js
Normal file
|
@ -0,0 +1,164 @@
|
|||
let changeDates = [];
|
||||
let items = [];
|
||||
|
||||
async function load() {
|
||||
items = await loadItems();
|
||||
items.sort((a, b) => {
|
||||
if (a.store < b.store) {
|
||||
return -1;
|
||||
} else if (a.store > b.store) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
} else if (a.name > b.name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
const dates = {};
|
||||
for (const item of items) {
|
||||
if (item.priceHistory.length == 1) continue;
|
||||
for (let i = 0; i < item.priceHistory.length; i++) {
|
||||
const price = item.priceHistory[i];
|
||||
if (i + 1 < item.priceHistory.length) {
|
||||
if (item.priceHistory[i].price != item.priceHistory[i + 1].price) dates[price.date] = dates[price.date] ? dates[price.date] + 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
const dateNames = Object.keys(dates).sort((a, b) => b.localeCompare(a));
|
||||
|
||||
const dateSelection = document.querySelector("#dates");
|
||||
dateNames.forEach((dateName, index) => {
|
||||
const option = dom("option", dateName + " (" + dates[dateName] + ")");
|
||||
option.setAttribute("value", dateName);
|
||||
if (index == 0) option.selected = true;
|
||||
dateSelection.appendChild(option);
|
||||
});
|
||||
|
||||
document.querySelectorAll("#type").forEach((changeType) => {
|
||||
changeType.addEventListener("change", () => showResults(items));
|
||||
});
|
||||
|
||||
dateSelection.addEventListener("change", () => {
|
||||
showResults(items);
|
||||
});
|
||||
|
||||
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.querySelector("#all").addEventListener("change", () => {
|
||||
const checked = filtersStore.querySelector("#all").checked;
|
||||
STORE_KEYS.forEach((store) => (filtersStore.querySelector(`#${store}`).checked = checked));
|
||||
showResults(items);
|
||||
});
|
||||
|
||||
document.querySelector("#filters-changes").innerHTML = `
|
||||
${customCheckbox(`increases`, "Teurer", true, "gray")}
|
||||
${customCheckbox(`decreases`, "Billiger", true, "gray")}
|
||||
`;
|
||||
|
||||
document.querySelectorAll("input").forEach((input) => {
|
||||
if (input.id == "all" && input.id != "type") return;
|
||||
input.addEventListener("change", () => showResults(items));
|
||||
});
|
||||
|
||||
let timeoutId;
|
||||
document.querySelector("#filter").addEventListener("input", () => {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(() => {
|
||||
if (document.querySelector("#filter").value.trim().startsWith("!")) {
|
||||
document.querySelector("#filters-store").classList.add("hidden");
|
||||
document.querySelector("#filters-changes").classList.add("hidden");
|
||||
} else {
|
||||
document.querySelector("#filters-store").classList.remove("hidden");
|
||||
document.querySelector("#filters-changes").classList.remove("hidden");
|
||||
}
|
||||
showResults(items);
|
||||
}, 50);
|
||||
});
|
||||
|
||||
showResults(items);
|
||||
}
|
||||
|
||||
function showResults(items) {
|
||||
const query = document.querySelector("#filter").value.trim();
|
||||
let today = null;
|
||||
if (document.querySelector("#date").checked) {
|
||||
today = document.querySelector("#dates").value;
|
||||
if (!query.startsWith("!")) document.querySelector("#filters-changes").classList.remove("hidden");
|
||||
} else {
|
||||
document.querySelector("#filters-changes").classList.add("hidden");
|
||||
}
|
||||
const increases = document.querySelector("#increases").checked;
|
||||
const decreases = document.querySelector("#decreases").checked;
|
||||
const storeCheckboxes = STORE_KEYS.map((store) => document.querySelector(`#${store}`));
|
||||
const checkedStores = STORE_KEYS.filter((store, i) => storeCheckboxes[i].checked);
|
||||
let changedItems = [];
|
||||
for (const item of items) {
|
||||
if (item.priceHistory.length < 2) continue;
|
||||
if (!checkedStores.includes(item.store)) continue;
|
||||
|
||||
if (today) {
|
||||
for (let i = 0; i < item.priceHistory.length; i++) {
|
||||
if (item.priceHistory[i].date == today && i + 1 < item.priceHistory.length) {
|
||||
if (increases && item.priceHistory[i].price > item.priceHistory[i + 1].price) {
|
||||
changedItems.push(item);
|
||||
}
|
||||
if (decreases && item.priceHistory[i].price < item.priceHistory[i + 1].price) {
|
||||
changedItems.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (item.priceHistory[0].price < item.priceHistory[1].price) changedItems.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
const total = changedItems.length;
|
||||
if (query.startsWith("!") || query.length >= 3)
|
||||
changedItems = searchItems(changedItems, document.querySelector("#filter").value, checkedStores, false, 0, 10000, false, false);
|
||||
document.querySelector("#numresults").innerText = "Resultate: " + changedItems.length + (total > changedItems.length ? " / " + total : "");
|
||||
const table = document.querySelector("#result");
|
||||
table.innerHTML = "";
|
||||
if (changedItems.length == 0) return;
|
||||
const header = dom(
|
||||
"thead",
|
||||
`<tr>
|
||||
<th class="text-center">Kette</th>
|
||||
<th>Name</th>
|
||||
<th class="flex text-nowrap">
|
||||
<span>Preis </span>
|
||||
<span class="expander">+</span>
|
||||
</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 == "+" ? "-" : "+";
|
||||
table.querySelectorAll(".priceinfo").forEach((el) => (showHideAll.showAll ? el.classList.remove("hidden") : el.classList.add("hidden")));
|
||||
showHideAll.showAll = !showHideAll.showAll;
|
||||
});
|
||||
|
||||
table.appendChild(header);
|
||||
|
||||
for (let item of changedItems) {
|
||||
item = JSON.parse(JSON.stringify(item));
|
||||
const itemDom = itemToDOM(item);
|
||||
table.appendChild(itemDom);
|
||||
}
|
||||
|
||||
table.classList.remove("hidden");
|
||||
}
|
||||
|
||||
load();
|
|
@ -2,24 +2,10 @@
|
|||
|
||||
<div class="w-full relative px-4 flex-1">
|
||||
<h1 class="text-2xl font-bold pb-2 pt-8 text-center">Preisänderungen</h1>
|
||||
<div class="bg-stone-200 rounded-xl p-4 max-w-4xl mx-auto md:mb-12 md:mt-6">
|
||||
<div class="filters mb-4 text-sm flex justify-center gap-4">
|
||||
<label>
|
||||
<input type="radio" id="date" name="type" value="date" checked /> Datum
|
||||
<select id="dates" class="bg-white rounded-full"></select>
|
||||
</label>
|
||||
<label><input type="radio" id="cheaper" name="type" value="cheaper" /> Billiger seit letzter Änderung</label>
|
||||
</div>
|
||||
<input id="filter" class="search rounded-lg px-2 py-1 w-full mb-4" type="text" placeholder="Filtern... (mind. 3 Zeichen)" />
|
||||
<div class="filters flex justify-center gap-2 flex-wrap" id="filters-store"></div>
|
||||
<div class="filters flex justify-center gap-2 mt-4" id="filters-changes"></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" id="numresults"></div>
|
||||
<table id="result" class="hidden w-full"></table>
|
||||
<items-filter x-id="items-filter" pricechanges pricedirection stores misc placeholder="Filtern..."></items-filter>
|
||||
<items-list chart></items-list>
|
||||
</div>
|
||||
|
||||
<script src="js/alasql.js"></script>
|
||||
<script src="utils.js"></script>
|
||||
<script src="changes.js"></script>
|
||||
|
||||
%%_templates/_footer.html%%
|
||||
|
|
173
site/changes.js
173
site/changes.js
|
@ -1,164 +1,11 @@
|
|||
let changeDates = [];
|
||||
let items = [];
|
||||
const model = require("./model");
|
||||
require("./views");
|
||||
|
||||
async function load() {
|
||||
items = await loadItems();
|
||||
items.sort((a, b) => {
|
||||
if (a.store < b.store) {
|
||||
return -1;
|
||||
} else if (a.store > b.store) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
} else if (a.name > b.name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
const dates = {};
|
||||
for (const item of items) {
|
||||
if (item.priceHistory.length == 1) continue;
|
||||
for (let i = 0; i < item.priceHistory.length; i++) {
|
||||
const price = item.priceHistory[i];
|
||||
if (i + 1 < item.priceHistory.length) {
|
||||
if (item.priceHistory[i].price != item.priceHistory[i + 1].price) dates[price.date] = dates[price.date] ? dates[price.date] + 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
const dateNames = Object.keys(dates).sort((a, b) => b.localeCompare(a));
|
||||
|
||||
const dateSelection = document.querySelector("#dates");
|
||||
dateNames.forEach((dateName, index) => {
|
||||
const option = dom("option", dateName + " (" + dates[dateName] + ")");
|
||||
option.setAttribute("value", dateName);
|
||||
if (index == 0) option.selected = true;
|
||||
dateSelection.appendChild(option);
|
||||
});
|
||||
|
||||
document.querySelectorAll("#type").forEach((changeType) => {
|
||||
changeType.addEventListener("change", () => showResults(items));
|
||||
});
|
||||
|
||||
dateSelection.addEventListener("change", () => {
|
||||
showResults(items);
|
||||
});
|
||||
|
||||
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.querySelector("#all").addEventListener("change", () => {
|
||||
const checked = filtersStore.querySelector("#all").checked;
|
||||
STORE_KEYS.forEach((store) => (filtersStore.querySelector(`#${store}`).checked = checked));
|
||||
showResults(items);
|
||||
});
|
||||
|
||||
document.querySelector("#filters-changes").innerHTML = `
|
||||
${customCheckbox(`increases`, "Teurer", true, "gray")}
|
||||
${customCheckbox(`decreases`, "Billiger", true, "gray")}
|
||||
`;
|
||||
|
||||
document.querySelectorAll("input").forEach((input) => {
|
||||
if (input.id == "all" && input.id != "type") return;
|
||||
input.addEventListener("change", () => showResults(items));
|
||||
});
|
||||
|
||||
let timeoutId;
|
||||
document.querySelector("#filter").addEventListener("input", () => {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(() => {
|
||||
if (document.querySelector("#filter").value.trim().startsWith("!")) {
|
||||
document.querySelector("#filters-store").classList.add("hidden");
|
||||
document.querySelector("#filters-changes").classList.add("hidden");
|
||||
} else {
|
||||
document.querySelector("#filters-store").classList.remove("hidden");
|
||||
document.querySelector("#filters-changes").classList.remove("hidden");
|
||||
}
|
||||
showResults(items);
|
||||
}, 50);
|
||||
});
|
||||
|
||||
showResults(items);
|
||||
}
|
||||
|
||||
function showResults(items) {
|
||||
const query = document.querySelector("#filter").value.trim();
|
||||
let today = null;
|
||||
if (document.querySelector("#date").checked) {
|
||||
today = document.querySelector("#dates").value;
|
||||
if (!query.startsWith("!")) document.querySelector("#filters-changes").classList.remove("hidden");
|
||||
} else {
|
||||
document.querySelector("#filters-changes").classList.add("hidden");
|
||||
}
|
||||
const increases = document.querySelector("#increases").checked;
|
||||
const decreases = document.querySelector("#decreases").checked;
|
||||
const storeCheckboxes = STORE_KEYS.map((store) => document.querySelector(`#${store}`));
|
||||
const checkedStores = STORE_KEYS.filter((store, i) => storeCheckboxes[i].checked);
|
||||
let changedItems = [];
|
||||
for (const item of items) {
|
||||
if (item.priceHistory.length < 2) continue;
|
||||
if (!checkedStores.includes(item.store)) continue;
|
||||
|
||||
if (today) {
|
||||
for (let i = 0; i < item.priceHistory.length; i++) {
|
||||
if (item.priceHistory[i].date == today && i + 1 < item.priceHistory.length) {
|
||||
if (increases && item.priceHistory[i].price > item.priceHistory[i + 1].price) {
|
||||
changedItems.push(item);
|
||||
}
|
||||
if (decreases && item.priceHistory[i].price < item.priceHistory[i + 1].price) {
|
||||
changedItems.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (item.priceHistory[0].price < item.priceHistory[1].price) changedItems.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
const total = changedItems.length;
|
||||
if (query.startsWith("!") || query.length >= 3)
|
||||
changedItems = searchItems(changedItems, document.querySelector("#filter").value, checkedStores, false, 0, 10000, false, false);
|
||||
document.querySelector("#numresults").innerText = "Resultate: " + changedItems.length + (total > changedItems.length ? " / " + total : "");
|
||||
const table = document.querySelector("#result");
|
||||
table.innerHTML = "";
|
||||
if (changedItems.length == 0) return;
|
||||
const header = dom(
|
||||
"thead",
|
||||
`<tr>
|
||||
<th class="text-center">Kette</th>
|
||||
<th>Name</th>
|
||||
<th class="flex text-nowrap">
|
||||
<span>Preis </span>
|
||||
<span class="expander">+</span>
|
||||
</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 == "+" ? "-" : "+";
|
||||
table.querySelectorAll(".priceinfo").forEach((el) => (showHideAll.showAll ? el.classList.remove("hidden") : el.classList.add("hidden")));
|
||||
showHideAll.showAll = !showHideAll.showAll;
|
||||
});
|
||||
|
||||
table.appendChild(header);
|
||||
|
||||
for (let item of changedItems) {
|
||||
item = JSON.parse(JSON.stringify(item));
|
||||
const itemDom = itemToDOM(item);
|
||||
table.appendChild(itemDom);
|
||||
}
|
||||
|
||||
table.classList.remove("hidden");
|
||||
}
|
||||
|
||||
load();
|
||||
(async () => {
|
||||
await model.load();
|
||||
const itemsFilter = document.querySelector("items-filter");
|
||||
const itemsList = document.querySelector("items-list");
|
||||
itemsList.elements.sort.value = "chain-and-name";
|
||||
itemsFilter.model = itemsList.model = model.items;
|
||||
itemsFilter.filter();
|
||||
})();
|
||||
|
|
|
@ -26,6 +26,6 @@
|
|||
<script src="js/alasql.js"></script>
|
||||
<script src="js/chart.js"></script>
|
||||
<script src="utils.js"></script>
|
||||
<script src="index.js"></script>
|
||||
<script src="index-old.js"></script>
|
||||
|
||||
%%_templates/_footer.html%%
|
||||
|
|
|
@ -69,30 +69,9 @@ class Items extends Model {
|
|||
return this._lookup;
|
||||
}
|
||||
|
||||
async load() {
|
||||
let start = performance.now();
|
||||
const compressedItemsPerStore = [];
|
||||
for (const store of STORE_KEYS) {
|
||||
compressedItemsPerStore.push(
|
||||
new Promise(async (resolve) => {
|
||||
const start = performance.now();
|
||||
try {
|
||||
const response = await fetch(`data/latest-canonical.${store}.compressed.json`);
|
||||
const json = await response.json();
|
||||
log(`Items - loading compressed items for ${store} took ${deltaTime(start)} secs`);
|
||||
resolve(decompress(json));
|
||||
} catch (e) {
|
||||
log(`Items - error while loading compressed items for ${store} ${e.message}`);
|
||||
resolve([]);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
const items = [].concat(...(await Promise.all(compressedItemsPerStore)));
|
||||
log(`Items - loaded ${items.length} items took ${deltaTime(start).toFixed(4)} secs`);
|
||||
|
||||
processItems(items) {
|
||||
const lookup = {};
|
||||
start = performance.now();
|
||||
const start = performance.now();
|
||||
for (const item of items) {
|
||||
lookup[item.store + item.id] = item;
|
||||
item.search = item.name + " " + item.quantity + " " + item.unit;
|
||||
|
@ -136,10 +115,34 @@ class Items extends Model {
|
|||
});
|
||||
|
||||
log(`Items - processing ${items.length} items took ${deltaTime(start).toFixed(4)} secs`);
|
||||
|
||||
this._items = items;
|
||||
this._lookup = lookup;
|
||||
}
|
||||
|
||||
async load() {
|
||||
let start = performance.now();
|
||||
const compressedItemsPerStore = [];
|
||||
for (const store of STORE_KEYS) {
|
||||
compressedItemsPerStore.push(
|
||||
new Promise(async (resolve) => {
|
||||
const start = performance.now();
|
||||
try {
|
||||
const response = await fetch(`data/latest-canonical.${store}.compressed.json`);
|
||||
const json = await response.json();
|
||||
log(`Items - loading compressed items for ${store} took ${deltaTime(start)} secs`);
|
||||
resolve(decompress(json));
|
||||
} catch (e) {
|
||||
log(`Items - error while loading compressed items for ${store} ${e.message}`);
|
||||
resolve([]);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
const items = [].concat(...(await Promise.all(compressedItemsPerStore)));
|
||||
log(`Items - loaded ${items.length} items took ${deltaTime(start).toFixed(4)} secs`);
|
||||
|
||||
this.processItems(items);
|
||||
}
|
||||
}
|
||||
|
||||
exports.Items = Items;
|
||||
|
|
Loading…
Reference in New Issue
Block a user