feat(tickets): center the overview, remove its filter row

The INC/CRQ overview is now a centered "today" snapshot — header + metric tiles
centered. Removed the overview filter row (Cluster/Status/Window/Apply/refresh):
the Ticket explorer below carries the ad-hoc filtering, so it was redundant.
incQs() is fixed to window=today; dropped initIncDropdowns + the removed listeners.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
david kiania 2026-06-26 16:06:15 +03:00
parent e58195073d
commit 9fb39aa992

View file

@ -225,8 +225,8 @@
}
/* INC metric strip */
.metric-row { display: flex; flex-wrap: wrap; gap: 26px; }
.metric { display: flex; flex-direction: column; min-width: 96px; }
.metric-row { display: flex; flex-wrap: wrap; gap: 26px; justify-content: center; }
.metric { display: flex; flex-direction: column; align-items: center; text-align: center; min-width: 96px; }
.metric b { font-size: 22px; line-height: 1.1; font-variant-numeric: tabular-nums; }
.metric b.accent { color: var(--accent); }
.metric .lbl { font-size: 9.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-top: 3px; }
@ -238,9 +238,7 @@
.sla-unknown { color: var(--muted); }
/* INC overview row: metric tiles (left) + filters (right) on one balanced row */
.tk-overview-row { display: flex; align-items: center; justify-content: space-between; gap: 16px 28px; flex-wrap: wrap; }
.tk-overview-metrics { min-width: 0; }
.tk-filters { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; margin-left: auto; }
.tk-overview-metrics { min-width: 0; text-align: center; }
.tk-filters .ff select, .tk-filters .ff input { min-width: 130px; }
/* Live vehicle DOM marker (ported from FleetNow) */
@ -411,34 +409,9 @@
</div>
<main id="tk-main">
<div class="card span12">
<div class="tk-overview-row">
<div class="tk-overview-metrics">
<h2><span id="tk-ds-ov">INC</span> overview <span class="count" id="tk-fresh"></span></h2>
<div class="metric-row" id="tk-metrics"><div class="empty">Loading…</div></div>
</div>
<div class="tk-filters">
<div class="ff">
<label for="tk-cluster">Cluster</label>
<select id="tk-cluster"><option value="">All clusters</option></select>
</div>
<div class="ff">
<label for="tk-status">Status</label>
<select id="tk-status"><option value="">All statuses</option></select>
</div>
<div class="ff">
<label for="tk-window">Window</label>
<select id="tk-window">
<option value="today" selected>Today</option>
<option value="week">This week</option>
<option value="month">This month</option>
<option value="custom">Custom range</option>
</select>
</div>
<div class="ff custom" id="tk-ff-start"><label for="tk-start">Start</label><input type="date" id="tk-start"></div>
<div class="ff custom" id="tk-ff-end"><label for="tk-end">End</label><input type="date" id="tk-end"></div>
<button class="btn" id="tk-apply" type="button">Apply</button>
<button class="btn ghost" id="tk-refresh" type="button" title="Reload"></button>
</div>
<div class="tk-overview-metrics">
<h2><span id="tk-ds-ov">INC</span> overview <span class="count" id="tk-fresh"></span></h2>
<div class="metric-row" id="tk-metrics"><div class="empty">Loading…</div></div>
</div>
</div>
@ -929,9 +902,9 @@ function setDataset(ds) {
const U = ds.toUpperCase();
$('tk-ds-ov').textContent = U;
$('tk-ds-map').textContent = U;
// per-dataset dropdowns + explorer results are stale → clear and re-init
incDropdownsInit = false; incFilterOptionsInit = false;
['tk-cluster', 'tk-status', 'tk-x-eng', 'tk-x-cluster'].forEach(resetSelect);
// per-dataset explorer dropdowns + results are stale → clear and re-init
incFilterOptionsInit = false;
['tk-x-eng', 'tk-x-cluster'].forEach(resetSelect);
$('tk-x-id').value = ''; $('tk-x-id-list').innerHTML = '';
$('tk-x-count').textContent = '';
$('tk-x-wrap').innerHTML = '<div class="empty">Search by ticket id, engineer, cluster, state and time.</div>';
@ -1009,7 +982,7 @@ let tkMap = null, tkPopup = null, tkLivePoll = null, tkClosureChart = null;
const tkMarkers = new Map(); // imei → maplibregl.Marker
const tkLayerState = { open: true, closed: true, vehicles: true };
let tkOwnerFilter = null; // when set, the closed layer is filtered to this engineer (drill-down)
let incData = null, incDropdownsInit = false, vehCount = 0;
let incData = null, vehCount = 0;
let DS = 'inc'; // active Tickets dataset (inc | crq) — drives /webhook/${DS}-* endpoints + labels
let tkLoadedDay = null; // EAT calendar day of the last INC load — drives the midnight auto-rollover
const eatDay = () => new Date().toLocaleDateString('en-CA', { timeZone: 'Africa/Nairobi' });
@ -1023,18 +996,10 @@ function eatShort(iso) {
}
function addDay(ymd) { const [y, m, d] = ymd.split('-').map(Number); return new Date(Date.UTC(y, m - 1, d + 1)).toISOString().slice(0, 10); }
// The overview is a fixed "today" snapshot now that its filter row is removed —
// ad-hoc cluster / status / time filtering lives in the Ticket explorer below.
function incQs() {
const p = new URLSearchParams();
if ($('tk-cluster').value) p.set('cluster', $('tk-cluster').value);
if ($('tk-status').value) p.set('status', $('tk-status').value);
const w = $('tk-window').value;
if (w === 'custom') {
if ($('tk-start').value) p.set('from', $('tk-start').value + 'T00:00:00+03:00');
if ($('tk-end').value) p.set('to', addDay($('tk-end').value) + 'T00:00:00+03:00'); // inclusive end
} else {
p.set('window', w);
}
return p.toString();
return 'window=today';
}
// Squircle + bolt map marker: a rounded squircle tapering to a bottom point (anchors on
@ -1125,14 +1090,8 @@ function renderIncMap() {
function initIncMap() {
if (tkMap) { tkMap.resize(); return; } // already built — just fix sizing
// Filter / control listeners (attached once, with the map).
$('tk-window').addEventListener('change', () => {
const custom = $('tk-window').value === 'custom';
$('tk-ff-start').classList.toggle('show', custom);
$('tk-ff-end').classList.toggle('show', custom);
});
$('tk-apply').addEventListener('click', loadInc);
$('tk-refresh').addEventListener('click', loadInc);
// Control listeners (attached once, with the map). The overview filter row was
// removed (it's a fixed "today" snapshot); the explorer carries its own filters.
$('tk-layers-toggle').addEventListener('click', () => $('tk-layers').classList.toggle('collapsed'));
// Ticket explorer controls
@ -1327,23 +1286,12 @@ async function loadIncSearch() {
}
}
// Populate the Cluster / Status filters from the first unfiltered response.
function initIncDropdowns(m) {
const fill = (id, obj) => {
const el = $(id);
Object.keys(obj || {}).filter(k => k !== '').sort().forEach(k => el.add(new Option(k, k)));
};
fill('tk-cluster', m.by_cluster);
fill('tk-status', m.by_status);
}
async function loadInc() {
$('tk-main').classList.add('loading');
try {
const j = await api(`/webhook/${DS}-dashboard?${incQs()}`);
incData = j;
tkLoadedDay = eatDay();
if (!incDropdownsInit && j.metrics) { initIncDropdowns(j.metrics); incDropdownsInit = true; }
renderIncMetrics(j.metrics, j.freshness);
renderIncMap(); // fan out co-located pins so open (vivid) + closed (faded) both show
buildIncLayers();