dynamic bar count (multiples of 28), header overflow fix, parentId index for faster chain lookups

This commit is contained in:
Meleki committed 2026-02-23 06:32:08 +01:00
1 parent e2c7adab90
commit 59c492c04f
3 files changed
+29 -7

No files matched your search

+17 -5
View File
@@ -11,7 +11,13 @@ const WEEKLY_BUDGET = 500; // $ pro Woche
// Anchor config - loaded from /api/settings, defaults: Thursday 23:00
let ANCHOR_DAY = 0; // 0=Sun, 1=Mon, ..., 4=Thu
let ANCHOR_HOUR = 0;
const BAR_COUNT = 50;
function getBarCount() {
const w = window.innerWidth || document.documentElement.clientWidth;
// ~12px per bar minimum, count must be divisible by 14
const raw = Math.floor(w / 12);
const count = Math.floor(raw / 28) * 28;
return Math.max(28, count);
}
const CHAIN_COLORS = [
'#4ade80','#60a5fa','#f97316','#c084fc','#f472b6',
'#facc15','#34d399','#818cf8','#fb7185','#a78bfa',
@@ -104,6 +110,12 @@ async function init() {
restoreFromHash();
await loadView();
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => loadView(), 250);
});
}
function saveToHash() {
@@ -147,7 +159,7 @@ async function loadView() {
renderEntryChart(entries);
} else {
const totalMinutes = (viewTo - viewFrom) / 60000;
const bucketMinutes = Math.max(1, Math.round(totalMinutes / BAR_COUNT));
const bucketMinutes = Math.max(1, Math.round(totalMinutes / getBarCount()));
const buckets = await api(`/api/cost-buckets?from=${enc(from)}&to=${enc(to)}&bucketMinutes=${bucketMinutes}`);
renderSummary(summary);
renderBucketChart(buckets, bucketMinutes);
@@ -181,7 +193,7 @@ function renderBucketChart(buckets, bucketMinutes) {
let lastWeekId = null;
let lastDayId = null;
for (let i = 0; i < BAR_COUNT; i++) {
for (let i = 0; i < getBarCount(); i++) {
const b = bucketMap[i];
const cost = b ? b.cost : 0;
const count = b ? b.count : 0;
@@ -226,7 +238,7 @@ function renderBucketChart(buckets, bucketMinutes) {
const lbl = document.createElement('div');
lbl.className = 'chart-label';
if (i % Math.ceil(BAR_COUNT / 6) === 0) lbl.textContent = fmtShort(bucketStart);
if (i % Math.ceil(getBarCount() / 6) === 0) lbl.textContent = fmtShort(bucketStart);
labels.appendChild(lbl);
}
}
@@ -332,7 +344,7 @@ function renderChainBlocks(chains) {
// Already at max zoom (1 Tag) - enter entry mode for the bucket containing this chain
const chainMid = new Date((new Date(c.first).getTime() + new Date(c.last).getTime()) / 2);
const totalMinutes = (viewTo - viewFrom) / 60000;
const bucketMinutes = Math.max(1, Math.round(totalMinutes / BAR_COUNT));
const bucketMinutes = Math.max(1, Math.round(totalMinutes / getBarCount()));
const bucketMs = bucketMinutes * 60000;
const elapsed = chainMid - viewFrom;
const bucketIndex = Math.floor(elapsed / bucketMs);
+4 -2
View File
@@ -30,8 +30,12 @@ header {
align-items: center;
gap: 24px;
flex-shrink: 0;
overflow-x: auto;
flex-wrap: nowrap;
}
header > * { flex-shrink: 0; }
header h1 { font-size: 1.1rem; font-weight: 600; color: var(--accent); }
#anchor-config {
@@ -197,8 +201,6 @@ header h1 { font-size: 1.1rem; font-weight: 600; color: var(--accent); }
font-size: 0.55rem;
color: var(--muted);
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}