← back to Agent Cabinet
Activity viewer: date-range filter — from/to date pickers + Today/7d/30d/All presets scope the feed by timestamp; composes with search + type chips; 'in range' count + active-preset highlight
c18a8e89f7cf61b11301f39a00804bdc23370259 · 2026-06-16 18:24:15 -0700 · SteveStudio2
Files touched
Diff
commit c18a8e89f7cf61b11301f39a00804bdc23370259
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Tue Jun 16 18:24:15 2026 -0700
Activity viewer: date-range filter — from/to date pickers + Today/7d/30d/All presets scope the feed by timestamp; composes with search + type chips; 'in range' count + active-preset highlight
---
activity.html | 39 +++++++++++++++++++++++++++++++++++----
1 file changed, 35 insertions(+), 4 deletions(-)
diff --git a/activity.html b/activity.html
index 8bb3040..b3a9417 100644
--- a/activity.html
+++ b/activity.html
@@ -21,6 +21,12 @@
#search:focus{outline:none;border-color:#34f4ce}
#search-x{color:var(--dim);font-size:12px;cursor:pointer;display:none}
#search-x.show{display:inline}
+ .daterow{margin-top:10px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px;color:var(--dim)}
+ .daterow input[type=date]{background:#0a0e16;border:1px solid #2b3650;color:#e7ecf3;border-radius:7px;padding:5px 9px;font-size:12px;color-scheme:dark}
+ .daterow input[type=date]:focus{outline:none;border-color:#34f4ce}
+ .pre{cursor:pointer;font-size:11.5px;padding:4px 10px;border-radius:14px;border:1px solid var(--line);background:#121826;color:#aeb8c8}
+ .pre:hover{border-color:#34f4ce;color:#fff}
+ .pre.on{background:#10302b;border-color:#34f4ce;color:#34f4ce}
main{max-width:1340px;margin:0 auto;padding:18px 28px 70px}
.cols{display:grid;grid-template-columns:var(--col-w,380px) 14px minmax(0,1fr);align-items:start}
.col-up{position:sticky;top:132px;max-height:calc(100vh - 150px);overflow:auto;padding-right:6px}
@@ -73,6 +79,9 @@
<div class="meta"><span class="pulse"></span><span id="meta">loading…</span> · auto-refreshes every 30s · <a href="/pyramid">← cabinet pyramid</a></div>
<div class="chips" id="chips"></div>
<div class="searchrow"><input id="search" type="search" placeholder="🔎 filter events & jobs — title, vendor, repo, skill…" autocomplete="off"><span id="search-x" onclick="clearSearch()">✕ clear</span></div>
+ <div class="daterow">📅 <input type="date" id="from"> <span>→</span> <input type="date" id="to">
+ <span class="pre" onclick="preset(0,this)">Today</span><span class="pre" onclick="preset(7,this)">7d</span><span class="pre" onclick="preset(30,this)">30d</span><span class="pre on" onclick="preset(null,this)">All</span>
+ <span id="range-n"></span></div>
</header>
<div id="det"><div class="det-head"><span id="det-title">—</span><button class="det-x" onclick="closeDet()">✕</button></div><div id="det-body"></div></div>
<main class="cols">
@@ -89,15 +98,16 @@ const TYPES={
tick:{label:'Watch ticks', col:'#60a5fa'},
};
const VCOL={FAIL:'#ef4444',WARN:'#fbbf24',PASS:'#34d399',NA:'#6b7280'};
-let DATA=[], OFF_FILTER=new Set(), QUERY=''; // types hidden + search query
+let DATA=[], OFF_FILTER=new Set(), QUERY='', FROM=null, TO=null; // type/text/date filters
function matchQ(e){ return !QUERY || ((e.title||'')+' '+(e.detail||'')+' '+(e.type||'')).toLowerCase().includes(QUERY); }
+function inRange(ts){ const t=Date.parse(ts); if(isNaN(t)) return true; return (FROM==null||t>=FROM)&&(TO==null||t<=TO); }
function colorFor(e){ if(e.type==='canary') return VCOL[e.verdict]||TYPES.canary.col; return (TYPES[e.type]||{}).col||'#2b3650'; }
function fmtTime(iso){ try{ return new Date(iso).toLocaleTimeString(undefined,{hour:'2-digit',minute:'2-digit'}); }catch(e){ return '--:--'; } }
function fmtDay(iso){ try{ return new Date(iso).toLocaleDateString(undefined,{weekday:'short',month:'short',day:'numeric',year:'numeric'}); }catch(e){ return iso; } }
function dayKey(iso){ try{ return new Date(iso).toISOString().slice(0,10); }catch(e){ return iso.slice(0,10); } }
function renderChips(){
- const set=DATA.filter(matchQ);
+ const set=DATA.filter(e=>matchQ(e)&&inRange(e.ts));
const counts={}; set.forEach(e=>counts[e.type]=(counts[e.type]||0)+1);
const c=document.getElementById('chips');
c.innerHTML='<span class="chip '+(OFF_FILTER.size?'':'on')+'" data-t="__all"><b>All</b> <span class="n">'+set.length+'</span></span>'+
@@ -109,9 +119,9 @@ function renderChips(){
});
}
function renderFeed(){
- const shown=DATA.filter(e=>!OFF_FILTER.has(e.type) && matchQ(e));
+ const shown=DATA.filter(e=>!OFF_FILTER.has(e.type) && matchQ(e) && inRange(e.ts));
const feed=document.getElementById('feed');
- if(!shown.length){ feed.innerHTML='<div class="empty">'+(QUERY?'No matches for “'+esc(QUERY)+'”.':'No events for this filter.')+'</div>'; return; }
+ if(!shown.length){ feed.innerHTML='<div class="empty">'+(QUERY||FROM||TO?'No matches for the current filters.':'No events for this filter.')+'</div>'; return; }
let html='', curDay=null;
shown.forEach(e=>{
const dk=dayKey(e.ts);
@@ -204,6 +214,27 @@ document.addEventListener('keydown', e=>{ if(e.key==='Escape') closeDet(); });
window.addEventListener('mouseup', ()=>{ if(!dragging) return; dragging=false; sp.classList.remove('drag'); document.body.style.userSelect=''; const w=parseInt(cols.style.getPropertyValue('--col-w'),10); if(w) localStorage.setItem(KEY,w); });
sp.addEventListener('dblclick', ()=>{ cols.style.setProperty('--col-w','380px'); localStorage.setItem(KEY,'380'); });
})();
+// date range filter
+function pad2(n){ return ('0'+n).slice(-2); }
+function fmtDI(d){ return d.getFullYear()+'-'+pad2(d.getMonth()+1)+'-'+pad2(d.getDate()); }
+function recomputeDates(){
+ const fv=document.getElementById('from').value, tv=document.getElementById('to').value;
+ FROM = fv ? Date.parse(fv+'T00:00:00') : null;
+ TO = tv ? Date.parse(tv+'T23:59:59') : null;
+ const set=DATA.filter(e=>matchQ(e)&&inRange(e.ts));
+ document.getElementById('range-n').textContent = (FROM||TO) ? (set.length+' in range') : '';
+ renderChips(); renderFeed();
+}
+function preset(days, el){
+ const f=document.getElementById('from'), t=document.getElementById('to');
+ if(days==null){ f.value=''; t.value=''; }
+ else { const now=new Date(); t.value=fmtDI(now); f.value=fmtDI(new Date(now.getTime()-days*86400000)); }
+ document.querySelectorAll('.pre').forEach(p=>p.classList.remove('on'));
+ if(el) el.classList.add('on');
+ recomputeDates();
+}
+['from','to'].forEach(id=>document.getElementById(id).addEventListener('change', ()=>{ document.querySelectorAll('.pre').forEach(p=>p.classList.remove('on')); recomputeDates(); }));
+
const _si=document.getElementById('search');
_si.addEventListener('input', ()=>{ QUERY=_si.value.trim().toLowerCase(); document.getElementById('search-x').classList.toggle('show', !!QUERY); renderChips(); renderFeed(); renderSchedule(); });
function clearSearch(){ _si.value=''; QUERY=''; document.getElementById('search-x').classList.remove('show'); _si.focus(); renderChips(); renderFeed(); renderSchedule(); }
← c791b73 Activity viewer: live search box — filters the feed AND sche
·
back to Agent Cabinet
·
Activity viewer: FIX day-grouping used UTC date while times d170113 →