Compare commits
6 Commits
67f3c28e82
...
v1.6.4
| Author | SHA1 | Date | |
|---|---|---|---|
| 5bda1dd616 | |||
| 3e333291c6 | |||
| aa62528862 | |||
| ac155d8843 | |||
| ed7465f5cd | |||
| 0eebcd0765 |
@@ -55,6 +55,9 @@ LLM_API_VERSION=
|
||||
# For local dev, start Valkey with: docker run -d -p 6379:6379 valkey/valkey:8-alpine
|
||||
REDIS_URL=redis://localhost:6379/0
|
||||
|
||||
# UI default page size (number of events shown per page)
|
||||
DEFAULT_PAGE_SIZE=24
|
||||
|
||||
# Optional: privacy / access control
|
||||
# Hide entire services from users without PRIVACY_SERVICE_ROLES
|
||||
# PRIVACY_SERVICES=Exchange,Teams
|
||||
|
||||
@@ -60,6 +60,9 @@ class Settings(BaseSettings):
|
||||
# Redis (caching + async job queue)
|
||||
REDIS_URL: str = "redis://localhost:6379/0"
|
||||
|
||||
# UI defaults
|
||||
DEFAULT_PAGE_SIZE: int = 24
|
||||
|
||||
|
||||
_settings = Settings()
|
||||
|
||||
@@ -100,3 +103,4 @@ PRIVACY_SENSITIVE_OPERATIONS = {o.strip() for o in _settings.PRIVACY_SENSITIVE_O
|
||||
PRIVACY_SERVICE_ROLES = {r.strip() for r in _settings.PRIVACY_SERVICE_ROLES.split(",") if r.strip()}
|
||||
|
||||
REDIS_URL = _settings.REDIS_URL
|
||||
DEFAULT_PAGE_SIZE = _settings.DEFAULT_PAGE_SIZE
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Admin Operations Center</title>
|
||||
<link rel="stylesheet" href="/style.css?v=9" />
|
||||
<link rel="stylesheet" href="/style.css?v=12" />
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script src="https://alcdn.msauth.net/browser/2.37.0/js/msal-browser.min.js" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
@@ -184,8 +184,8 @@
|
||||
<template x-for="(evt, idx) in askEvents" :key="evt.id || idx">
|
||||
<article class="event event--compact">
|
||||
<div class="event__meta">
|
||||
<span class="pill" x-text="evt.display_category || evt.service || '—'"></span>
|
||||
<span class="pill" :class="['success','succeeded','ok','passed','true'].includes((evt.result || '').toLowerCase()) ? 'pill--ok' : 'pill--warn'" x-text="evt.result || '—'"></span>
|
||||
<span class="pill pill--clickable" x-text="evt.display_category || evt.service || '—'" @click="filterByService(evt.service || evt.display_category)" title="Filter by this service"></span>
|
||||
<span class="pill pill--clickable" :class="['success','succeeded','ok','passed','true'].includes((evt.result || '').toLowerCase()) ? 'pill--ok' : 'pill--warn'" x-text="evt.result || '—'" @click="filterByResult(evt.result)" title="Filter by this result"></span>
|
||||
</div>
|
||||
<h3 x-text="evt.operation || '—'"></h3>
|
||||
<p class="event__detail" x-show="evt.display_summary"><strong>Summary:</strong> <span x-text="evt.display_summary"></span></p>
|
||||
@@ -211,8 +211,8 @@
|
||||
<template x-for="(evt, idx) in events" :key="evt._id || evt.id || idx">
|
||||
<article class="event">
|
||||
<div class="event__meta">
|
||||
<span class="pill" x-text="evt.display_category || evt.service || '—'"></span>
|
||||
<span class="pill" :class="['success','succeeded','ok','passed','true'].includes((evt.result || '').toLowerCase()) ? 'pill--ok' : 'pill--warn'" x-text="evt.result || '—'"></span>
|
||||
<span class="pill pill--clickable" x-text="evt.display_category || evt.service || '—'" @click="filterByService(evt.service || evt.display_category)" title="Filter by this service"></span>
|
||||
<span class="pill pill--clickable" :class="['success','succeeded','ok','passed','true'].includes((evt.result || '').toLowerCase()) ? 'pill--ok' : 'pill--warn'" x-text="evt.result || '—'" @click="filterByResult(evt.result)" title="Filter by this result"></span>
|
||||
</div>
|
||||
<h3 x-text="evt.operation || '—'"></h3>
|
||||
<p class="event__detail" x-show="evt.display_summary"><strong>Summary:</strong> <span x-text="evt.display_summary"></span></p>
|
||||
@@ -277,7 +277,7 @@
|
||||
<a :href="docsUrl" target="_blank" rel="noopener">📖 Documentation</a>
|
||||
</div>
|
||||
<div class="footer__right">
|
||||
<span>Built with ❤️ by CQRE</span>
|
||||
<span>Built with ❤️ by CQRE.NET</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
@@ -305,7 +305,7 @@
|
||||
accessToken: null,
|
||||
authScopes: [],
|
||||
filters: {
|
||||
actor: '', selectedServices: [], search: '', operation: '', result: '', start: '', end: '', limit: 100, includeTags: '', excludeTags: '',
|
||||
actor: '', selectedServices: [], search: '', operation: '', result: '', start: '', end: '', limit: 24, includeTags: '', excludeTags: '',
|
||||
},
|
||||
options: { actors: [], services: [], operations: [], results: [] },
|
||||
savedSearches: [],
|
||||
@@ -396,6 +396,11 @@
|
||||
if (featRes.ok) {
|
||||
const featBody = await featRes.json();
|
||||
this.aiFeaturesEnabled = featBody.ai_features_enabled !== false;
|
||||
if (featBody.default_page_size) {
|
||||
this.filters.limit = featBody.default_page_size;
|
||||
} else {
|
||||
this.filters.limit = 24;
|
||||
}
|
||||
} else {
|
||||
this.aiFeaturesEnabled = true;
|
||||
}
|
||||
@@ -564,9 +569,8 @@
|
||||
|
||||
const saved = localStorage.getItem('aoc_filters');
|
||||
if (!saved && this.options.services.length) {
|
||||
// Default: exclude noisy high-volume services
|
||||
const noisy = ['Exchange', 'SharePoint', 'Teams'];
|
||||
this.filters.selectedServices = this.options.services.filter((s) => !noisy.includes(s));
|
||||
// Default: show all services (privacy controls handle exclusions server-side)
|
||||
this.filters.selectedServices = [...this.options.services];
|
||||
} else if (saved) {
|
||||
try {
|
||||
const parsed = JSON.parse(saved);
|
||||
@@ -660,8 +664,23 @@
|
||||
},
|
||||
|
||||
clearFilters() {
|
||||
const noisy = ['Exchange', 'SharePoint', 'Teams'];
|
||||
this.filters = { actor: '', selectedServices: this.options.services.filter((s) => !noisy.includes(s)), search: '', operation: '', result: '', start: '', end: '', limit: 100, includeTags: '', excludeTags: '' };
|
||||
this.filters = { actor: '', selectedServices: [...this.options.services], search: '', operation: '', result: '', start: '', end: '', limit: 24, includeTags: '', excludeTags: '' };
|
||||
this.saveFilters();
|
||||
this.resetPagination();
|
||||
this.loadEvents();
|
||||
},
|
||||
|
||||
filterByService(service) {
|
||||
if (!service) return;
|
||||
this.filters.selectedServices = [service];
|
||||
this.saveFilters();
|
||||
this.resetPagination();
|
||||
this.loadEvents();
|
||||
},
|
||||
|
||||
filterByResult(result) {
|
||||
if (!result) return;
|
||||
this.filters.result = this.filters.result === result ? '' : result;
|
||||
this.saveFilters();
|
||||
this.resetPagination();
|
||||
this.loadEvents();
|
||||
|
||||
@@ -355,6 +355,27 @@ input {
|
||||
border-color: rgba(239, 68, 68, 0.5);
|
||||
}
|
||||
|
||||
.pill--clickable {
|
||||
cursor: pointer;
|
||||
transition: transform 0.1s ease, box-shadow 0.15s ease, background 0.15s ease;
|
||||
}
|
||||
|
||||
.pill--clickable:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(125, 211, 252, 0.2);
|
||||
background: rgba(125, 211, 252, 0.2);
|
||||
}
|
||||
|
||||
.pill--clickable.pill--ok:hover {
|
||||
box-shadow: 0 2px 8px rgba(34, 197, 94, 0.2);
|
||||
background: rgba(34, 197, 94, 0.25);
|
||||
}
|
||||
|
||||
.pill--clickable.pill--warn:hover {
|
||||
box-shadow: 0 2px 8px rgba(249, 115, 22, 0.2);
|
||||
background: rgba(249, 115, 22, 0.25);
|
||||
}
|
||||
|
||||
.event h3 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 17px;
|
||||
|
||||
@@ -4,6 +4,7 @@ from config import (
|
||||
AUTH_ENABLED,
|
||||
AUTH_SCOPE,
|
||||
AUTH_TENANT_ID,
|
||||
DEFAULT_PAGE_SIZE,
|
||||
)
|
||||
from fastapi import APIRouter
|
||||
|
||||
@@ -25,4 +26,5 @@ def auth_config():
|
||||
def features_config():
|
||||
return {
|
||||
"ai_features_enabled": AI_FEATURES_ENABLED,
|
||||
"default_page_size": DEFAULT_PAGE_SIZE,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user