Improve performance

This commit is contained in:
Florian Bauer 2023-06-07 23:37:53 +02:00
parent 0554e742e6
commit 4961995839
8 changed files with 1748 additions and 5321 deletions

View File

@ -8,7 +8,8 @@
"dev": "NODE_ENV=development PORT=$PORT node server.js",
"start": "NODE_ENV=production PORT=$PORT node server.js",
"format": "npx prettier --write .",
"build": "npx tailwindcss -o site/style.css --minify"
"build": "npx tailwindcss -i site/tailwind.css -o site/style.css --minify",
"css": "npx tailwindcss -i site/tailwind.css -o site/style.css --watch"
},
"repository": {
"type": "git",

View File

@ -1,9 +1,6 @@
const shoppingCarts = new ShoppingCarts();
shoppingCarts.load();
const buttonClass =
"border w-6 h-6 flex items-center justify-center bg-white hover:shadow cursor-pointer rounded leading-none hover:scale-110 transition-transform duration-200";
async function load() {
const items = await loadItems();
const lookup = {};
@ -68,9 +65,9 @@ async function load() {
const canvasDom = document.querySelector("#chart");
document.querySelector("#sum-container").innerHTML = customCheckbox(`sum`, "Preissumme Gesamt", true, "gray", "gray");
document.querySelector("#sumstores-container").innerHTML = customCheckbox(`sumstores`, "Preissumme pro Kette", true, "gray", "gray");
document.querySelector("#todayonly-container").innerHTML = customCheckbox(`todayonly`, "Nur heutige Preise", false, "gray", "gray");
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)));
@ -83,9 +80,9 @@ async function load() {
const filtersStore = document.querySelector("#filters-store");
filtersStore.innerHTML = `
${customCheckbox("all", "<strong>Alle</strong>", true, "gray", "gray")}
${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, "gray")
customCheckbox(store, stores[store].name, stores[store].name.toLowerCase().endsWith("de") ? false : true, stores[store].color)
).join(" ")}
`;
@ -131,7 +128,7 @@ function showSearch(cart, items) {
return header;
},
(item, itemDom) => {
const cell = dom("td", `<input type="button" class="ml-auto ${buttonClass}" value="+">`);
const cell = dom("td", `<input type="button" class="ml-auto btn-action" value="+">`);
cell.children[0].addEventListener("click", () => {
cart.items.push(item);
shoppingCarts.save();
@ -201,7 +198,7 @@ function showCart(cart) {
itemTable.innerHTML = "";
header = dom(
"thead",
`<tr class="bg-primary text-white text-left hidden md:table-row uppercase text-sm">
`<tr>
<th class="text-center">Kette</th>
<th>Name</th>
<th>Preis <span class="expander">+</span></th>
@ -226,14 +223,14 @@ function showCart(cart) {
`
<label>
<input type="checkbox" class="hidden peer">
<span class="peer-checked:bg-blue-700 bg-transparent rounded p-1 text-sm">📈</span>
<span class="peer-checked:bg-blue-700 btn-action">📈</span>
</label>
<input type="button" class="ml-auto ${buttonClass}" value="-">
<input type="button" class="${buttonClass}" value="▲">
<input type="button" class="${buttonClass}" value="▼">
<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("order-5", "flex", "items-center", "p-1");
cell.classList.add("action");
if (cartItem.chart) cell.children[0].setAttribute("checked", true);
cell.children[0].addEventListener("change", () => {

View File

@ -49,9 +49,9 @@ async function load() {
const filtersStore = document.querySelector("#filters-store");
filtersStore.innerHTML = `
${customCheckbox(`all`, "<strong>Alle</strong>", true, "gray", "gray")}
${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, "gray")
customCheckbox(store, stores[store].name, stores[store].name.toLowerCase().endsWith("de") ? false : true, stores[store].color)
).join(" ")}
`;
filtersStore.querySelector("#all").addEventListener("change", () => {
@ -61,8 +61,8 @@ async function load() {
});
document.querySelector("#filters-changes").innerHTML = `
${customCheckbox(`increases`, "Teurer", true, "gray", "gray")}
${customCheckbox(`decreases`, "Billiger", true, "gray", "gray")}
${customCheckbox(`increases`, "Teurer", true, "gray")}
${customCheckbox(`decreases`, "Billiger", true, "gray")}
`;
document.querySelectorAll("input").forEach((input) => {
@ -131,7 +131,7 @@ function showResults(items) {
if (changedItems.length == 0) return;
const header = dom(
"thead",
`<tr class="bg-primary text-left hidden md:table-row uppercase text-sm text-white">
`<tr>
<th class="text-center">Kette</th>
<th>Name</th>
<th class="flex text-nowrap">

View File

@ -19,9 +19,9 @@ async function load() {
const canvasDom = chartDom.querySelector("canvas");
let lastHits = null;
document.querySelector("#sum-container").innerHTML = customCheckbox(`sum`, "Preissumme Gesamt", false, "gray", "gray");
document.querySelector("#sumstores-container").innerHTML = customCheckbox(`sumstores`, "Preissumme pro Kette", false, "gray", "gray");
document.querySelector("#todayonly-container").innerHTML = customCheckbox(`todayonly`, "Nur heutige Preise", false, "gray", "gray");
document.querySelector("#sum-container").innerHTML = customCheckbox(`sum`, "Preissumme Gesamt", false, "gray");
document.querySelector("#sumstores-container").innerHTML = customCheckbox(`sumstores`, "Preissumme pro Kette", false, "gray");
document.querySelector("#todayonly-container").innerHTML = customCheckbox(`todayonly`, "Nur heutige Preise", false, "gray");
document.querySelector("#sum").addEventListener("change", () => updateCharts(canvasDom, lastHits));
document.querySelector("#sumstores").addEventListener("change", () => updateCharts(canvasDom, lastHits));
@ -63,10 +63,9 @@ async function load() {
"td",
`<label class="flex">
<input class="hidden peer" type="checkbox" ${checked ? "checked" : ""} data-id="${dataId}">
<span class="ml-auto peer-checked:bg-blue-700 bg-transparent rounded p-1 text-sm transform group-[.decreased]:scale-x-flip hover:scale-110 cursor-pointer">📈</span>
<span class="ml-auto peer-checked:bg-blue-700 group-[.decreased]:scale-x-flip btn-action">📈</span>
</label>`
);
cell.classList.add("order-4", "text-right");
itemDom.appendChild(cell);
const handleClick = (eventShouldSetQuery = false) => {

File diff suppressed because it is too large Load Diff

View File

@ -5,3 +5,171 @@
canvas.hidden {
display: none !important;
}
/* Product table */
thead > tr {
@apply bg-primary text-white text-left hidden md:table-row uppercase text-sm;
}
.item {
@apply mb-3 overflow-hidden;
@apply grid grid-cols-3 col-span-3;
@apply md:table-row;
@apply border border-b rounded-xl;
}
.item > td[data-label="Kette"] {
@apply p-1 uppercase font-medium align-top;
@apply md:text-center;
@apply order-2;
}
.item > td[data-label="Name"] {
@apply p-1 font-bold text-gray-800;
@apply md:font-normal md:bg-white;
@apply md:hover:bg-gray-100;
@apply order-1 col-span-3;
}
.item > td[data-label="Preis"] {
@apply p-1 text-left whitespace-nowrap align-top z-20;
@apply order-3;
}
.item > td:nth-child(4) {
@apply order-4 text-right;
}
.item > td.action {
@apply order-5 flex items-center p-1;
}
.item .chevron {
@apply text-sm cursor-pointer;
}
/* additional info table */
.priceinfo {
@apply text-xs mt-2;
@apply md:text-sm;
}
.priceinfo tr > td:nth-child(1) {
@apply font-medium;
}
.priceinfo .price-line {
@apply text-xs text-white px-1;
@apply md:text-sm;
}
.btn-action {
@apply w-6 h-6;
@apply flex items-center justify-center bg-white;
@apply cursor-pointer hover:shadow hover:scale-110;
@apply border rounded leading-none;
@apply transition-transform duration-200;
}
/* colors */
.item.red {
@apply bg-red-200/50;
@apply border-red-200;
}
.item.yellow {
@apply bg-yellow-200/50;
@apply border-yellow-200;
}
.item.green {
@apply bg-green-200/50;
@apply border-green-200;
}
.item.purple {
@apply bg-purple-200/50;
@apply border-purple-200;
}
.item.pink {
@apply bg-pink-200/50;
@apply border-pink-200;
}
.item.rose {
@apply bg-rose-200/50;
@apply border-rose-200;
}
.item.orange {
@apply bg-orange-200/50;
@apply border-orange-200;
}
.item.blue {
@apply bg-blue-200/50;
@apply border-blue-200;
}
.item.purple {
@apply bg-purple-200/50;
@apply border-purple-200;
}
.item.teal {
@apply bg-teal-200/50;
@apply border-teal-200;
}
.item.stone {
@apply bg-gray-200/50;
@apply border-gray-200;
}
/* checkbox */
.customcheckbox {
@apply inline-flex items-center gap-x-1 px-2 py-1;
@apply text-xs font-medium text-gray-600;
@apply cursor-pointer hover:bg-gray-400 hover:scale-105;
@apply border rounded-full bg-gray-200 border-gray-400;
@apply transition-all duration-200;
}
/* colors */
.customcheckbox.red {
@apply bg-red-200;
@apply border-red-400;
@apply hover:bg-red-400;
}
.customcheckbox.yellow {
@apply bg-yellow-200;
@apply border-yellow-400;
@apply hover:bg-yellow-400;
}
.customcheckbox.green {
@apply bg-green-200;
@apply border-green-400;
@apply hover:bg-green-400;
}
.customcheckbox.purple {
@apply bg-purple-200;
@apply border-purple-400;
@apply hover:bg-purple-400;
}
.customcheckbox.pink {
@apply bg-pink-200;
@apply border-pink-400;
@apply hover:bg-pink-400;
}
.customcheckbox.rose {
@apply bg-rose-200;
@apply border-rose-400;
@apply hover:bg-rose-400;
}
.customcheckbox.orange {
@apply bg-orange-200;
@apply border-orange-400;
@apply hover:bg-orange-400;
}
.customcheckbox.blue {
@apply bg-blue-200;
@apply border-blue-400;
@apply hover:bg-blue-400;
}
.customcheckbox.purple {
@apply bg-purple-200;
@apply border-purple-400;
@apply hover:bg-purple-400;
}
.customcheckbox.teal {
@apply bg-teal-200;
@apply border-teal-400;
@apply hover:bg-teal-400;
}
.customcheckbox.stone {
@apply bg-stone-200;
@apply border-stone-400;
@apply hover:bg-stone-400;
}

View File

@ -300,10 +300,10 @@ function itemToDOM(item) {
const increase = Math.round(((currPrice - lastPrice) / lastPrice) * 100);
priceHistory += `<tr>
<td class="font-medium">${date}</td>
<td>${date}</td>
<td>
<div style="width: ${priceBase * currPrice}px"
class="text-xs md:text-sm text-white px-1 ${increase > 0 ? "bg-red-500" : "bg-green-500"}">
class="price-line ${increase > 0 ? "bg-red-500" : "bg-green-500"}">
${currPrice}
</div>
</td>
@ -320,39 +320,33 @@ function itemToDOM(item) {
const row = dom(
"tr",
`
<td class="md:text-center p-1 order-2 uppercase font-medium align-top" data-label="Kette">${item.store}</td>
<td class="font-bold md:font-normal text-gray-800 md:bg-white p-1 order-1 col-span-3 hover:bg-gray-100" data-label="Name">
<div class="flex items-center">${itemToStoreLink(item)} <small class="ml-auto">${
(item.isWeighted ? "⚖ " : "") + `${quantity} ${unit}`
}</small></div>
<table class="priceinfo hidden text-xs md:text-sm mt-2" aria-hidden="true">
<td data-label="Kette">${item.store}</td>
<td data-label="Name">
<div class="flex items-center">
${itemToStoreLink(item)}
<small class="ml-auto">
${(item.isWeighted ? "⚖ " : "") + `${quantity} ${unit}`}
</small>
</div>
<table class="priceinfo hidden" aria-hidden="true">
${priceHistory}
</table>
</td>
<td class="p-1 order-3 text-left whitespace-nowrap align-top z-20" data-label="Preis">
<td data-label="Preis">
<span> ${Number(item.price).toFixed(2)}</span>
<span class="${percentageChange > 0 ? "text-red-500" : percentageChange < 0 ? "text-green-500" : "hidden"}">
${percentageChange > 0 ? "+" + percentageChange : percentageChange}%
</span>
${item.priceHistory.length > 1 ? "(" + (item.priceHistory.length - 1) + ")" : ""}
<span class="text-sm cursor-pointer chevron"></span>
<span class="chevron"></span>
</td>
`
);
row.classList.add(
"bg-" + stores[item.store]?.color + "-200/50",
"grid",
"grid-cols-3",
"col-span-3",
"md:table-row",
"border-b",
"border-" + stores[item.store]?.color + "-200",
"rounded-xl",
"mb-3",
"border",
"overflow-hidden",
"item",
"group",
stores[item.store]?.color,
percentageChange > 0 ? "increased" : percentageChange < 0 ? "decreased" : "neutral"
);
@ -516,12 +510,12 @@ function searchItems(items, query, checkedStores, budgetBrands, minPrice, maxPri
return hits;
}
function customCheckbox(id, label, checked, bgColor, color) {
function customCheckbox(id, label, checked, bgColor) {
let isChecked = typeof checked === "boolean" ? (checked ? "checked" : "") : checked;
return `
<label class="cursor-pointer inline-flex items-center gap-x-1 rounded-full bg-${bgColor}-200 border border-${bgColor}-400 hover:bg-${bgColor}-400 px-2 py-1 text-xs font-medium text-${color}-600 transition-all duration-200 hover:scale-105">
<label class="customcheckbox ${bgColor}">
<input id="${id}" type="checkbox" ${isChecked} class="hidden peer">
<svg class="h-2 w-2 stroke-${color}-600 fill-${color}-100 peer-checked:fill-${color}-600" viewBox="0 0 6 6">
<svg class="h-2 w-2 stroke-gray-600 fill-gray-100 peer-checked:fill-gray-600" viewBox="0 0 6 6">
<circle cx="3" cy="3" r="2" />
</svg>
${label}

View File

@ -1,12 +1,6 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./site/**/*.{html,js}"],
safelist: [
{
pattern: /(bg|border|text)-(yellow|green|purple|pink|rose|orange|blue|purple|teal|stone)-(200)/,
variants: ["md", "lg", "hover"],
},
],
content: ["./site/**/*.{html,js,css}"],
theme: {
extend: {
colors: {