feat: events date filter and preserve list page in URL (SAC 0.7.1)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PTah
2026-06-01 10:32:24 +10:00
parent de29270a25
commit 06a8ed8614
8 changed files with 230 additions and 36 deletions
+80
View File
@@ -0,0 +1,80 @@
/** Query params for /events list — preserved when opening event detail and going back. */
import type { LocationQuery } from "vue-router";
export interface EventsListState {
page: number;
q: string;
severity: string;
type: string;
hostname: string;
dateFrom: string;
dateTo: string;
}
const LIST_KEYS = ["page", "q", "severity", "type", "hostname", "from", "to"] as const;
function queryString(value: unknown): string {
if (typeof value === "string") return value;
if (Array.isArray(value) && typeof value[0] === "string") return value[0];
return "";
}
export function parseEventsListQuery(query: LocationQuery): EventsListState {
const pageRaw = parseInt(queryString(query.page), 10);
return {
page: Number.isFinite(pageRaw) && pageRaw > 0 ? pageRaw : 1,
q: queryString(query.q),
severity: queryString(query.severity),
type: queryString(query.type),
hostname: queryString(query.hostname),
dateFrom: queryString(query.from),
dateTo: queryString(query.to),
};
}
export function buildEventsListQuery(state: EventsListState): Record<string, string> {
const q: Record<string, string> = {};
if (state.page > 1) q.page = String(state.page);
if (state.q.trim()) q.q = state.q.trim();
if (state.severity) q.severity = state.severity;
if (state.type.trim()) q.type = state.type.trim();
if (state.hostname.trim()) q.hostname = state.hostname.trim();
if (state.dateFrom) q.from = state.dateFrom;
if (state.dateTo) q.to = state.dateTo;
return q;
}
/** Subset of detail route query to restore the events list (excludes `from=reports`). */
export function eventsBackQueryFromDetail(query: LocationQuery): Record<string, string> {
const out: Record<string, string> = {};
for (const key of LIST_KEYS) {
const v = queryString(query[key]);
if (v) out[key] = v;
}
return out;
}
export function apiParamsFromState(state: EventsListState, pageSize: number): URLSearchParams {
const params = new URLSearchParams({
page: String(state.page),
page_size: String(pageSize),
});
if (state.severity) params.set("severity", state.severity);
if (state.type.trim()) params.set("type", state.type.trim());
if (state.hostname.trim()) params.set("hostname", state.hostname.trim());
if (state.q.trim()) params.set("q", state.q.trim());
if (state.dateFrom) params.set("from", state.dateFrom);
if (state.dateTo) params.set("to", state.dateTo);
return params;
}
export function eventsListQueryMatches(state: EventsListState, query: LocationQuery): boolean {
const built = buildEventsListQuery(state);
const fromRoute = buildEventsListQuery(parseEventsListQuery(query));
const keys = new Set([...Object.keys(built), ...Object.keys(fromRoute)]);
for (const key of keys) {
if ((built[key] ?? "") !== (fromRoute[key] ?? "")) return false;
}
return true;
}