config modal: server-side settings (budget, anchor, bar width), removed localStorage
This commit is contained in:
1 parent
59c492c04f
commit
6666129dfa
5 files changed
+157
-57
No files matched your search
+56
-32
@@ -6,15 +6,13 @@ const ZOOM_LEVELS = [
|
||||
{ name: '1 Woche', weeks: 1 },
|
||||
{ name: '1 Tag', days: 1 }
|
||||
];
|
||||
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;
|
||||
// Config - loaded from server
|
||||
let CFG = { weeklyBudget: 500, anchorDay: 0, anchorHour: 0, minBarWidth: 24 };
|
||||
|
||||
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 raw = Math.floor(w / CFG.minBarWidth);
|
||||
const count = Math.floor(raw / 28) * 28;
|
||||
return Math.max(28, count);
|
||||
}
|
||||
@@ -33,9 +31,9 @@ let entryMode = false;
|
||||
function getLastAnchor(date) {
|
||||
const d = new Date(date);
|
||||
// Walk back to the anchor weekday
|
||||
const diff = (d.getDay() - ANCHOR_DAY + 7) % 7;
|
||||
const diff = (d.getDay() - CFG.anchorDay + 7) % 7;
|
||||
d.setDate(d.getDate() - diff);
|
||||
d.setHours(ANCHOR_HOUR, 0, 0, 0);
|
||||
d.setHours(CFG.anchorHour, 0, 0, 0);
|
||||
// If we landed in the future, go back one week
|
||||
if (d > date) d.setDate(d.getDate() - 7);
|
||||
return d;
|
||||
@@ -72,11 +70,15 @@ async function init() {
|
||||
$('btn-sync').addEventListener('click', syncData);
|
||||
$('modal-close').addEventListener('click', closeModal);
|
||||
$('modal').addEventListener('click', e => { if (e.target === $('modal')) closeModal(); });
|
||||
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeModal(); });
|
||||
|
||||
// Anchor dropdowns
|
||||
const daySelect = $('anchor-day');
|
||||
const hourSelect = $('anchor-hour');
|
||||
// Config modal
|
||||
$('btn-config').addEventListener('click', openConfigModal);
|
||||
$('config-close').addEventListener('click', closeConfigModal);
|
||||
$('config-modal').addEventListener('click', e => { if (e.target === $('config-modal')) closeConfigModal(); });
|
||||
$('cfg-save').addEventListener('click', saveConfig);
|
||||
|
||||
// Populate hour dropdown
|
||||
const hourSelect = $('cfg-anchor-hour');
|
||||
for (let h = 0; h < 24; h++) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = h;
|
||||
@@ -84,24 +86,18 @@ async function init() {
|
||||
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;
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === 'Escape') { closeModal(); closeConfigModal(); }
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
// Load config from server
|
||||
try {
|
||||
const serverCfg = await api('/api/config');
|
||||
if (serverCfg.weeklyBudget != null) CFG.weeklyBudget = serverCfg.weeklyBudget;
|
||||
if (serverCfg.anchorDay != null) CFG.anchorDay = serverCfg.anchorDay;
|
||||
if (serverCfg.anchorHour != null) CFG.anchorHour = serverCfg.anchorHour;
|
||||
if (serverCfg.minBarWidth != null) CFG.minBarWidth = serverCfg.minBarWidth;
|
||||
} catch {}
|
||||
|
||||
zoomIndex = 0;
|
||||
const range = getAnchoredRange(zoomIndex);
|
||||
@@ -118,6 +114,34 @@ async function init() {
|
||||
});
|
||||
}
|
||||
|
||||
function openConfigModal() {
|
||||
$('cfg-budget').value = CFG.weeklyBudget;
|
||||
$('cfg-anchor-day').value = CFG.anchorDay;
|
||||
$('cfg-anchor-hour').value = CFG.anchorHour;
|
||||
$('cfg-bar-width').value = CFG.minBarWidth;
|
||||
$('config-modal').classList.remove('hidden');
|
||||
}
|
||||
|
||||
function closeConfigModal() {
|
||||
$('config-modal').classList.add('hidden');
|
||||
}
|
||||
|
||||
async function saveConfig() {
|
||||
CFG.weeklyBudget = Number($('cfg-budget').value) || 500;
|
||||
CFG.anchorDay = parseInt($('cfg-anchor-day').value);
|
||||
CFG.anchorHour = parseInt($('cfg-anchor-hour').value);
|
||||
CFG.minBarWidth = Number($('cfg-bar-width').value) || 12;
|
||||
|
||||
await fetch('/api/config', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(CFG)
|
||||
});
|
||||
|
||||
closeConfigModal();
|
||||
resetToStart();
|
||||
}
|
||||
|
||||
function saveToHash() {
|
||||
const state = {
|
||||
f: viewFrom.toISOString(),
|
||||
@@ -207,7 +231,7 @@ function renderBucketChart(buckets, bucketMinutes) {
|
||||
const weekId = weekAnchor.getTime();
|
||||
// Use local time for day boundary at anchor hour
|
||||
const localDayStart = new Date(bucketStart);
|
||||
localDayStart.setHours(localDayStart.getHours() - ANCHOR_HOUR);
|
||||
localDayStart.setHours(localDayStart.getHours() - CFG.anchorHour);
|
||||
const dayId = localDayStart.toLocaleDateString();
|
||||
const isNewWeek = lastWeekId !== null && weekId !== lastWeekId;
|
||||
const showDaySep = ZOOM_LEVELS[zoomIndex].weeks === 1;
|
||||
@@ -651,11 +675,11 @@ function escAttr(s) {
|
||||
}
|
||||
|
||||
function pct(cost) {
|
||||
return (cost / WEEKLY_BUDGET * 100).toFixed(3) + '%';
|
||||
return (cost / CFG.weeklyBudget * 100).toFixed(3) + '%';
|
||||
}
|
||||
|
||||
function pctScaled(cost) {
|
||||
const days = (viewTo - viewFrom) / 86400000;
|
||||
const budget = WEEKLY_BUDGET / 7 * days;
|
||||
const budget = CFG.weeklyBudget / 7 * days;
|
||||
return (cost / budget * 100).toFixed(3) + '%';
|
||||
}
|
||||
Reference in new issue
Block a user