feat: operational panel UI - active-scope dashboard, drill-down cards, scoped Agents/Blockers/Jobs, PT-BR labels, responsive CSS
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+20
-10
@@ -14,7 +14,7 @@ from . import agent_distribution, client_sync, config, enrollment, sso
|
||||
from .config import settings
|
||||
from .blocker_resolution import BlockerResolutionUnavailable, _company_key, resolve_blocker_with_context
|
||||
from .context_keys import ALLOWED_KEYS, ENUMS
|
||||
from .control_plane import blocker_detail, blocker_filter_summary, dashboard_summary, installation_view, list_blockers, list_installations, list_jobs, list_panel_events
|
||||
from .control_plane import blocker_detail, blocker_filter_summary, dashboard_summary, installation_view, jobs_status_summary, list_blockers, list_installations, list_jobs, list_panel_events
|
||||
from .human_auth import create_session, current_session, require_csrf, verify_password
|
||||
from .panel_presentation import format_ui_datetime
|
||||
from .models import AdminUser, Agent, AgentEnrollment, AgentRelease, AgentSnapshot, BlockedProduct, FiscalContextEntry, Installation, Job, Tenant
|
||||
@@ -162,11 +162,15 @@ def router(session_factory):
|
||||
finally: session.close()
|
||||
|
||||
@routes.get("/agents", response_class=HTMLResponse)
|
||||
def agents(request: Request, page: int = 1, search: str | None = None, health: str | None = None, sort: str = "display_name"):
|
||||
def agents(request: Request, page: int = 1, search: str | None = None, health: str | None = None, sort: str = "display_name", scope: str = "active", version: str | None = None):
|
||||
session, auth_data, redirect = page_auth(request)
|
||||
if redirect: return redirect
|
||||
try:
|
||||
return render(request, "agents.html", {"result": list_installations(session, page=page, page_size=50, search=search, health=health, sort=sort), "auth": auth_data, "search": search or "", "health_filter": health or "", "sort": sort, "query_base": query_base(search=search, health=health, sort=sort)})
|
||||
scope = scope if scope in ("active", "all") else "active"
|
||||
health = health if health in ("ONLINE", "DEGRADED", "OFFLINE", "FAILED", "UNKNOWN") else None
|
||||
sort = sort if sort in ("display_name", "worst_coverage") else "display_name"
|
||||
version = version if version == "outdated" else None
|
||||
return render(request, "agents.html", {"result": list_installations(session, page=page, page_size=50, search=search, health=health, sort=sort, scope=scope, version=version), "auth": auth_data, "search": search or "", "health_filter": health or "", "sort": sort, "scope_filter": scope, "version_filter": version or "", "query_base": query_base(search=search, health=health, sort=sort, scope=scope, version=version)})
|
||||
finally: session.close()
|
||||
|
||||
# --- Static /agents/* routes MUST be declared before /agents/{agent_id}:
|
||||
@@ -278,8 +282,8 @@ def router(session_factory):
|
||||
events = events[:25]
|
||||
for event in events:
|
||||
event["occurred_display"] = format_ui_datetime(datetime.fromisoformat(event["occurred_at"]) if event["occurred_at"] else None)
|
||||
agent_blockers = list_blockers(session, tenant_id=installation.tenant_id, agent_id=agent_id, page=1, page_size=25)
|
||||
return render(request, "agent_detail.html", {"detail": detail, "tab": tab, "blockers": agent_blockers, "blocker_breakdown": blocker_filter_summary(session, tenant_id=installation.tenant_id, agent_id=agent_id, status="OPEN")["blocker_breakdown"], "jobs": list_jobs(session, agent_id=agent_id, page=1, page_size=25), "events": events, "event_page": max(1, event_page), "has_more": has_more, "severity": severity or "", "event_type": event_type or "", "job_id": job_id or "", "product_code": product_code or "", "since": since or "", "until": until or "", "event_query_base": query_base(tab=tab, severity=severity, event_type=event_type, job_id=job_id, product_code=product_code, since=since, until=until), "auth": auth_data, "csrf_token": request.cookies.get("gti_csrf", ""), "ops": _ops_view(session, installation, agent)})
|
||||
agent_blockers = list_blockers(session, tenant_id=installation.tenant_id, agent_id=agent_id, page=1, page_size=25, scope="all")
|
||||
return render(request, "agent_detail.html", {"detail": detail, "tab": tab, "blockers": agent_blockers, "blocker_breakdown": blocker_filter_summary(session, tenant_id=installation.tenant_id, agent_id=agent_id, status="OPEN", scope="all")["blocker_breakdown"], "jobs": list_jobs(session, agent_id=agent_id, page=1, page_size=25, scope="all"), "events": events, "event_page": max(1, event_page), "has_more": has_more, "severity": severity or "", "event_type": event_type or "", "job_id": job_id or "", "product_code": product_code or "", "since": since or "", "until": until or "", "event_query_base": query_base(tab=tab, severity=severity, event_type=event_type, job_id=job_id, product_code=product_code, since=since, until=until), "auth": auth_data, "csrf_token": request.cookies.get("gti_csrf", ""), "ops": _ops_view(session, installation, agent)})
|
||||
finally: session.close()
|
||||
|
||||
def _ops_view(session, installation, agent):
|
||||
@@ -463,14 +467,16 @@ def router(session_factory):
|
||||
finally: session.close()
|
||||
|
||||
@routes.get("/blockers", response_class=HTMLResponse)
|
||||
def blockers(request: Request, page: int = 1, client: str | None = None, installation: str | None = None, field: str | None = None, blocker_type: str | None = None, status: str = "OPEN", age: str | None = None, search: str | None = None, agent_id: int | None = None, product_code: str | None = None):
|
||||
def blockers(request: Request, page: int = 1, client: str | None = None, installation: str | None = None, field: str | None = None, blocker_type: str | None = None, reason_code: str | None = None, status: str = "OPEN", age: str | None = None, search: str | None = None, agent_id: int | None = None, product_code: str | None = None, scope: str = "active"):
|
||||
session, auth_data, redirect = page_auth(request)
|
||||
if redirect: return redirect
|
||||
try:
|
||||
filters = {"agent_id": agent_id, "product_code": product_code, "client": client, "installation": installation, "field": field, "blocker_type": blocker_type, "status": status, "age": age, "search": search}
|
||||
scope = scope if scope in ("active", "all") else "active"
|
||||
status = status if status in ("OPEN", "RESOLVED", "ALL") else "OPEN"
|
||||
filters = {"agent_id": agent_id, "product_code": product_code, "client": client, "installation": installation, "field": field, "blocker_type": blocker_type, "reason_code": reason_code, "status": status, "age": age, "search": search, "scope": scope}
|
||||
result = list_blockers(session, page=page, page_size=50, **filters)
|
||||
summary = blocker_filter_summary(session, **filters)
|
||||
return render(request, "blockers.html", {"result": result, "summary": summary, "auth": auth_data, "csrf_token": request.cookies.get("gti_csrf", ""), "client_filter": client or "", "installation_filter": installation or "", "field_filter": field or "", "blocker_type_filter": blocker_type or "", "status_filter": status or "OPEN", "age_filter": age or "", "search": search or "", "query_base": query_base(**filters)})
|
||||
return render(request, "blockers.html", {"result": result, "summary": summary, "auth": auth_data, "csrf_token": request.cookies.get("gti_csrf", ""), "client_filter": client or "", "installation_filter": installation or "", "field_filter": field or "", "blocker_type_filter": blocker_type or "", "reason_code_filter": reason_code or "", "status_filter": status, "scope_filter": scope, "age_filter": age or "", "search": search or "", "query_base": query_base(**filters), "field_query_base": query_base(**{key: value for key, value in filters.items() if key != "field"})})
|
||||
finally: session.close()
|
||||
|
||||
@routes.get("/blockers/{blocker_id}", response_class=HTMLResponse)
|
||||
@@ -516,10 +522,14 @@ def router(session_factory):
|
||||
session.close()
|
||||
|
||||
@routes.get("/jobs", response_class=HTMLResponse)
|
||||
def jobs(request: Request, page: int = 1, status: str | None = None, search: str | None = None, kind: str | None = None, job_id: int | None = None):
|
||||
def jobs(request: Request, page: int = 1, status: str | None = None, search: str | None = None, kind: str | None = None, job_id: int | None = None, scope: str = "active"):
|
||||
session, auth_data, redirect = page_auth(request)
|
||||
if redirect: return redirect
|
||||
try: return render(request, "jobs.html", {"result": list_jobs(session, page=page, page_size=50, status=status, search=search, kind=kind, job_id=job_id), "auth": auth_data, "status_filter": status or "", "search": search or "", "kind_filter": kind or "", "query_base": query_base(status=status, search=search, kind=kind, job_id=job_id)})
|
||||
try:
|
||||
scope = scope if scope in ("active", "all") else "active"
|
||||
status = status if status in ("PENDING", "RUNNING", "COMPLETED", "FAILED") else None
|
||||
result = list_jobs(session, page=page, page_size=50, status=status, search=search, kind=kind, job_id=job_id, scope=scope)
|
||||
return render(request, "jobs.html", {"result": result, "status_counts": jobs_status_summary(session, scope=scope), "auth": auth_data, "status_filter": status or "", "scope_filter": scope, "search": search or "", "kind_filter": kind or "", "job_id_filter": job_id, "query_base": query_base(status=status, search=search, kind=kind, job_id=job_id, scope=scope)})
|
||||
finally: session.close()
|
||||
|
||||
return routes
|
||||
|
||||
Reference in New Issue
Block a user