UX: unify time selection and simplify controls\n\n- Replace separate Interval + Window with a single Time preset (Last hour/24h/7d/30d/12w/12m/All time)\n- Map presets to sensible grouping (hourly/daily/weekly/monthly) based on available intervals\n- Keep backward compatibility: preserve existing URL/state params; keep legacy controls hidden\n- Add client support for new windows (12w, 12m) in time-bucket slicing\n- Show only relevant controls per tab (Trends: smoothing; Distribution: percent/group/exclude)\n- Streamline reset flow to a sane default (Last 7 days)
This commit is contained in:
parent
6de85b7cc5
commit
95e54359d7
1 changed files with 102 additions and 31 deletions
|
@ -21,6 +21,7 @@
|
|||
</div>
|
||||
|
||||
<div id="controls" class="field is-grouped is-align-items-center mb-4" style="position: sticky; top: 0; background: white; z-index: 2; padding: 0.5rem 0;">
|
||||
<!-- Hidden native interval control kept for compatibility and availability probing -->
|
||||
<div id="interval-control" class="control has-icons-left is-hidden">
|
||||
<div class="select is-small">
|
||||
<select id="interval-select">
|
||||
|
@ -42,17 +43,20 @@
|
|||
</div>
|
||||
<span class="icon is-small is-left"><img src="icons/server.svg" alt="Domain"></span>
|
||||
</div>
|
||||
<div id="window-control" class="control has-icons-left is-hidden">
|
||||
<!-- Unified Time control: selects both range and sensible grouping -->
|
||||
<div id="time-control" class="control has-icons-left is-hidden">
|
||||
<div class="select is-small">
|
||||
<select id="window-select">
|
||||
<option value="1h">Last 1h</option>
|
||||
<option value="24h">Last 24h</option>
|
||||
<option value="7d" selected>Last 7d</option>
|
||||
<option value="30d">Last 30d</option>
|
||||
<option value="all">All</option>
|
||||
<select id="time-select">
|
||||
<option value="1h">Last hour</option>
|
||||
<option value="24h">Last 24 hours</option>
|
||||
<option value="7d" selected>Last 7 days</option>
|
||||
<option value="30d">Last 30 days</option>
|
||||
<option value="12w">Last 12 weeks</option>
|
||||
<option value="12m">Last 12 months</option>
|
||||
<option value="all">All time</option>
|
||||
</select>
|
||||
</div>
|
||||
<span class="icon is-small is-left"><img src="icons/pulse.svg" alt="Window"></span>
|
||||
<span class="icon is-small is-left"><img src="icons/clock.svg" alt="Time"></span>
|
||||
</div>
|
||||
<div id="smooth-control" class="control is-hidden">
|
||||
<label class="checkbox is-small">
|
||||
|
@ -130,7 +134,8 @@
|
|||
const domainSelect = document.getElementById('domain-select');
|
||||
const intervalControl = document.getElementById('interval-control');
|
||||
const domainControl = document.getElementById('domain-control');
|
||||
const windowControl = document.getElementById('window-control');
|
||||
const timeControl = document.getElementById('time-control');
|
||||
const timeSelect = document.getElementById('time-select');
|
||||
const modePercentControl = document.getElementById('mode-percent-control');
|
||||
const modeGroupControl = document.getElementById('mode-group-control');
|
||||
const excludeUncachedControl = document.getElementById('exclude-uncached-control');
|
||||
|
@ -163,7 +168,25 @@
|
|||
const elapsedElem = document.getElementById('stat-elapsed');
|
||||
|
||||
// Extra controls
|
||||
// Legacy window select kept for internal state only (not shown)
|
||||
const windowSelect = document.getElementById('window-select');
|
||||
|
||||
// If legacy window select is not present in DOM, create a hidden one for code paths
|
||||
// that still reference it.
|
||||
(function ensureHiddenWindowSelect(){
|
||||
if (!windowSelect) {
|
||||
const hidden = document.createElement('select');
|
||||
hidden.id = 'window-select';
|
||||
hidden.classList.add('is-hidden');
|
||||
// Supported values used by code
|
||||
['1h','24h','7d','30d','12w','12m','all'].forEach(v => {
|
||||
const o = document.createElement('option');
|
||||
o.value = v; o.textContent = v;
|
||||
hidden.appendChild(o);
|
||||
});
|
||||
document.body.appendChild(hidden);
|
||||
}
|
||||
})();
|
||||
const percentToggle = document.getElementById('percent-toggle');
|
||||
const groupToggle = document.getElementById('group-toggle');
|
||||
const excludeUncachedToggle = document.getElementById('exclude-uncached-toggle');
|
||||
|
@ -172,7 +195,7 @@
|
|||
let currentInterval = intervalSelect.value;
|
||||
let currentDomain = domainSelect.value;
|
||||
let currentTab = 'recent';
|
||||
let currentWindow = windowSelect.value; // 1h, 24h, 7d, 30d, all
|
||||
let currentWindow = windowSelect ? windowSelect.value : '7d'; // 1h, 24h, 7d, 30d, 12w, 12m, all
|
||||
let modePercent = false;
|
||||
let modeGroup = true;
|
||||
let excludeUncached = true;
|
||||
|
@ -241,10 +264,46 @@
|
|||
case '24h': return interval === 'hourly' ? 24 : 'all';
|
||||
case '7d': return interval === 'daily' ? 7 : 'all';
|
||||
case '30d': return interval === 'daily' ? 30 : 'all';
|
||||
case '12w': return interval === 'weekly' ? 12 : 'all';
|
||||
case '12m': return interval === 'monthly' ? 12 : 'all';
|
||||
default: return 'all';
|
||||
}
|
||||
}
|
||||
|
||||
function availableIntervals() {
|
||||
try {
|
||||
return Array.from(intervalSelect ? intervalSelect.options : []).map(o => o.value);
|
||||
} catch { return []; }
|
||||
}
|
||||
|
||||
function pickIntervalForWindow(win) {
|
||||
const avail = availableIntervals();
|
||||
const pref = (list) => list.find(x => avail.includes(x));
|
||||
switch (win) {
|
||||
case '1h':
|
||||
case '24h':
|
||||
return pref(['hourly','daily','weekly','monthly']) || (avail[0] || 'daily');
|
||||
case '7d':
|
||||
case '30d':
|
||||
return pref(['daily','weekly','monthly','hourly']) || (avail[0] || 'daily');
|
||||
case '12w':
|
||||
return pref(['weekly','daily','monthly']) || (avail[0] || 'weekly');
|
||||
case '12m':
|
||||
return pref(['monthly','weekly','daily']) || (avail[0] || 'monthly');
|
||||
default:
|
||||
// all time: favor coarser buckets if available
|
||||
return pref(['monthly','weekly','daily','hourly']) || (avail[0] || 'weekly');
|
||||
}
|
||||
}
|
||||
|
||||
function applyTimePreset(win) {
|
||||
currentWindow = win;
|
||||
currentInterval = pickIntervalForWindow(win);
|
||||
if (intervalSelect) intervalSelect.value = currentInterval;
|
||||
const winSel = document.getElementById('window-select');
|
||||
if (winSel) winSel.value = currentWindow;
|
||||
}
|
||||
|
||||
function initReport(token, rep, base) {
|
||||
fetch(base + '/' + rep.json, { signal: token.controller.signal })
|
||||
.then(r => r.json())
|
||||
|
@ -517,11 +576,12 @@
|
|||
Object.entries(sections).forEach(([key, section]) => {
|
||||
section.classList.toggle('is-hidden', key !== name);
|
||||
});
|
||||
const showInterval = name !== 'recent' && name !== 'analysis';
|
||||
const showDomain = showInterval;
|
||||
intervalControl.classList.toggle('is-hidden', !showInterval);
|
||||
const showTime = name !== 'recent' && name !== 'analysis';
|
||||
const showDomain = showTime;
|
||||
// Always keep legacy interval control hidden; use unified time control
|
||||
intervalControl.classList.add('is-hidden');
|
||||
domainControl.classList.toggle('is-hidden', !showDomain);
|
||||
windowControl.classList.toggle('is-hidden', !showInterval);
|
||||
timeControl.classList.toggle('is-hidden', !showTime);
|
||||
// Only show percent/group/exclude toggles on Distribution tab,
|
||||
// and smoothing only on Trends tab
|
||||
modePercentControl.classList.toggle('is-hidden', name !== 'distribution');
|
||||
|
@ -539,13 +599,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
intervalSelect.addEventListener('change', () => {
|
||||
currentInterval = intervalSelect.value;
|
||||
abortLoad(currentLoad);
|
||||
Object.values(containers).forEach(reset);
|
||||
updateURL();
|
||||
loadReports();
|
||||
});
|
||||
if (intervalSelect) {
|
||||
intervalSelect.addEventListener('change', () => {
|
||||
currentInterval = intervalSelect.value;
|
||||
abortLoad(currentLoad);
|
||||
Object.values(containers).forEach(reset);
|
||||
updateURL();
|
||||
loadReports();
|
||||
});
|
||||
}
|
||||
|
||||
domainSelect.addEventListener('change', () => {
|
||||
currentDomain = domainSelect.value;
|
||||
|
@ -555,12 +617,14 @@
|
|||
loadReports();
|
||||
});
|
||||
|
||||
windowSelect.addEventListener('change', () => {
|
||||
currentWindow = windowSelect.value;
|
||||
abortLoad(currentLoad);
|
||||
updateURL();
|
||||
loadReports();
|
||||
});
|
||||
if (timeSelect) {
|
||||
timeSelect.addEventListener('change', () => {
|
||||
applyTimePreset(timeSelect.value);
|
||||
abortLoad(currentLoad);
|
||||
updateURL();
|
||||
loadReports();
|
||||
});
|
||||
}
|
||||
|
||||
percentToggle.addEventListener('change', () => {
|
||||
modePercent = percentToggle.checked;
|
||||
|
@ -602,9 +666,10 @@
|
|||
} catch {}
|
||||
// Reset to hard defaults
|
||||
currentTab = 'recent';
|
||||
currentInterval = intervalSelect.value = intervalSelect.options[0]?.value || currentInterval;
|
||||
currentInterval = intervalSelect ? (intervalSelect.value = intervalSelect.options[0]?.value || currentInterval) : currentInterval;
|
||||
currentDomain = domainSelect.value = '';
|
||||
currentWindow = windowSelect.value = '7d';
|
||||
applyTimePreset('7d');
|
||||
if (timeSelect) timeSelect.value = '7d';
|
||||
modePercent = percentToggle.checked = false;
|
||||
modeGroup = groupToggle.checked = true;
|
||||
excludeUncached = excludeUncachedToggle.checked = true;
|
||||
|
@ -616,9 +681,15 @@
|
|||
loadSavedState();
|
||||
applyURLParams();
|
||||
// Sync controls
|
||||
intervalSelect.value = currentInterval;
|
||||
if (intervalSelect) intervalSelect.value = currentInterval;
|
||||
domainSelect.value = currentDomain;
|
||||
windowSelect.value = currentWindow;
|
||||
// Sync unified time select based on state
|
||||
if (timeSelect) {
|
||||
const known = new Set(['1h','24h','7d','30d','12w','12m','all']);
|
||||
const pick = known.has(currentWindow) ? currentWindow : 'all';
|
||||
timeSelect.value = pick;
|
||||
applyTimePreset(pick);
|
||||
}
|
||||
percentToggle.checked = modePercent;
|
||||
groupToggle.checked = modeGroup;
|
||||
excludeUncachedToggle.checked = excludeUncached;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue