diff --git a/Program.cs b/Program.cs index 78d556c..90b2f46 100644 --- a/Program.cs +++ b/Program.cs @@ -15,14 +15,6 @@ var app = builder.Build(); app.UseDefaultFiles(); app.UseStaticFiles(); -// GET /api/settings -app.MapGet("/api/settings", (IConfiguration config) => -{ - var anchorDay = int.TryParse(config["AnchorDay"], out var d) ? d : 0; - var anchorHour = int.TryParse(config["AnchorHour"], out var h) ? h : 0; - return Results.Ok(new { anchorDay, anchorHour }); -}); - // GET /api/cost-buckets?from=&to=&bucketMinutes= // Returns N buckets of aggregated cost app.MapGet("/api/cost-buckets", (Func factory, string from, string to, int bucketMinutes) => diff --git a/appsettings.json b/appsettings.json index f5695e5..5ba55b3 100644 --- a/appsettings.json +++ b/appsettings.json @@ -7,7 +7,5 @@ }, "AllowedHosts": "*", "Urls": "http://0.0.0.0:18684", - "DbPath": "../jsonl-sync/sessions.db", - "AnchorDay": 0, - "AnchorHour": 0 + "DbPath": "../jsonl-sync/sessions.db" } diff --git a/wwwroot/app.js b/wwwroot/app.js index dcf4f9b..7fe0f3b 100644 --- a/wwwroot/app.js +++ b/wwwroot/app.js @@ -68,12 +68,34 @@ async function init() { $('modal').addEventListener('click', e => { if (e.target === $('modal')) closeModal(); }); document.addEventListener('keydown', e => { if (e.key === 'Escape') closeModal(); }); - // Load anchor settings - try { - const settings = await api('/api/settings'); - if (settings.anchorDay != null) ANCHOR_DAY = settings.anchorDay; - if (settings.anchorHour != null) ANCHOR_HOUR = settings.anchorHour; - } catch {} + // Anchor dropdowns + const daySelect = $('anchor-day'); + const hourSelect = $('anchor-hour'); + for (let h = 0; h < 24; h++) { + const opt = document.createElement('option'); + opt.value = h; + opt.textContent = h.toString().padStart(2, '0') + ':00'; + hourSelect.appendChild(opt); + } + + // Load from localStorage + const savedDay = localStorage.getItem('anchorDay'); + const savedHour = localStorage.getItem('anchorHour'); + if (savedDay != null) ANCHOR_DAY = parseInt(savedDay); + if (savedHour != null) ANCHOR_HOUR = parseInt(savedHour); + daySelect.value = ANCHOR_DAY; + hourSelect.value = ANCHOR_HOUR; + + daySelect.addEventListener('change', () => { + ANCHOR_DAY = parseInt(daySelect.value); + localStorage.setItem('anchorDay', ANCHOR_DAY); + resetToStart(); + }); + hourSelect.addEventListener('change', () => { + ANCHOR_HOUR = parseInt(hourSelect.value); + localStorage.setItem('anchorHour', ANCHOR_HOUR); + resetToStart(); + }); zoomIndex = 0; const range = getAnchoredRange(zoomIndex); @@ -445,6 +467,17 @@ function enterEntryMode(bucketStart, bucketEnd) { loadView(); } +function resetToStart() { + location.hash = ''; + zoomIndex = 0; + entryMode = false; + zoomStack = []; + const range = getAnchoredRange(zoomIndex); + viewFrom = range.from; + viewTo = range.to; + loadView(); +} + function zoomOut() { if (zoomStack.length) { const prev = zoomStack.pop(); diff --git a/wwwroot/index.html b/wwwroot/index.html index 61a895a..7ed6006 100644 --- a/wwwroot/index.html +++ b/wwwroot/index.html @@ -10,6 +10,15 @@

Cost Analytics

+
+ + + +
diff --git a/wwwroot/style.css b/wwwroot/style.css index e239f96..7da3382 100644 --- a/wwwroot/style.css +++ b/wwwroot/style.css @@ -34,6 +34,23 @@ header { header h1 { font-size: 1.1rem; font-weight: 600; color: var(--accent); } +#anchor-config { + display: flex; + align-items: center; + gap: 6px; + font-size: 0.8rem; + color: var(--muted); +} + +#anchor-config select { + background: var(--surface); + color: var(--text); + border: 1px solid var(--border); + border-radius: 4px; + padding: 2px 4px; + font-size: 0.8rem; +} + #btn-sync { background: var(--border); color: var(--text);