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:
gti
2026-09-24 13:38:40 -03:00
co-authored by Claude Sonnet 5
parent bfd4d28aa4
commit 9a142975a6
13 changed files with 725 additions and 88 deletions
+72 -13
View File
@@ -80,11 +80,11 @@ def test_dashboard_renders_operational_queue_labels_and_human_dates():
response = client.get("/")
assert response.status_code == 200
for label in ("Agents total", "Online", "Degraded", "Offline", "Failed", "Unknown"):
for label in ("Servidores", "Online", "Atenção", "Offline", "Falhou", "Não informado"):
assert label in response.text
assert "Produtos com blockers abertos" in response.text
assert "Blockers abertos" in response.text
assert "ATENÇÃO NECESSÁRIA" in response.text
assert "Atenção necessária" in response.text
assert "Ver todos" in response.text
assert "Jobs com falha" in response.text
assert "Agents desatualizados" in response.text
@@ -103,6 +103,68 @@ def test_dashboard_coverage_displays_reconciled_product_buckets():
assert "Cobertura reconciliada" in response.text
def test_agents_scope_querystring_distinguishes_active_inventory_from_history():
client, _, _ = _panel_client()
with client._session_factory() as session:
tenant = session.scalar(select(Tenant).where(Tenant.name == "Acme"))
tenant.active = False
session.commit()
current = client.get("/agents?scope=active")
history = client.get("/agents?scope=all")
assert current.status_code == history.status_code == 200
assert "Acme ERP" not in current.text
assert "Acme ERP" in history.text
def test_blockers_and_jobs_scope_routes_keep_history_and_server_filters():
client, agent_id, _ = _panel_client()
with client._session_factory() as session:
agent = session.get(Agent, agent_id)
agent.enabled = False
session.commit()
assert "P-001" not in client.get("/blockers?scope=active&field=CFOP&reason_code=MISSING_CONTEXT").text
assert "P-001" in client.get("/blockers?scope=all&field=CFOP&reason_code=MISSING_CONTEXT").text
assert "Acme ERP" not in client.get("/jobs?scope=active&status=RUNNING").text
assert "Acme ERP" in client.get("/jobs?scope=all&status=RUNNING").text
def test_dashboard_links_reach_scoped_health_blocker_failed_job_and_outdated_filters():
client, agent_id, _ = _panel_client()
with client._session_factory() as session:
installation = session.scalar(select(Installation).where(Installation.agent_id == agent_id))
upsert_blocker(session, installation.tenant_id, {"company": "Acme", "product_code": "P-NCM", "description": "Mouse GTI", "blocker_type": "BLOCKED_MISSING_EVIDENCE", "field": "NCM", "reason_code": "INVALID_NCM", "input_fingerprint": "ncm-link"}, installation_id=installation.id)
session.add(Job(agent_id=agent_id, kind="SYNC_INCREMENTAL", status="FAILED", created_at=datetime.now(timezone.utc), error="falha longa"))
session.commit()
home = client.get("/").text
assert 'href="/agents?scope=active"' in home
assert 'href="/agents?scope=active&amp;health=ONLINE"' in home
assert 'href="/blockers?scope=active&amp;status=OPEN&amp;field=NCM"' in home
assert 'href="/jobs?scope=active&amp;status=FAILED"' in home
assert 'href="/agents?scope=active&amp;version=outdated"' in home
def test_list_filters_are_visible_and_clearable_with_ptbr_statuses():
client, _, _ = _panel_client()
agents = client.get("/agents?scope=active&health=ONLINE").text
blockers = client.get("/blockers?scope=active&field=CFOP").text
jobs = client.get("/jobs?scope=active&status=RUNNING").text
assert 'name="health"' in agents and 'value="ONLINE" selected' in agents
assert 'href="/agents?scope=active"' in agents and "Limpar filtros" in agents
assert "Saúde técnica: Online" not in agents
assert 'name="field"' in blockers and 'value="CFOP" selected' in blockers
assert "Evidência fiscal ausente" in blockers and "Prioridade: HIGH" not in blockers
assert 'href="/blockers?scope=active"' in blockers and "Limpar filtros" in blockers
assert 'value="RUNNING" selected' in jobs and "Executando" in jobs
assert 'href="/jobs?scope=active"' in jobs and "Limpar filtros" in jobs
def test_login_has_centered_card_and_explicit_field_labels():
client, _, _ = _panel_client()
@@ -126,12 +188,10 @@ def test_dashboard_lists_bounded_failed_job_rows_with_identity_error_age_and_emp
response = client.get("/")
assert response.status_code == 200
assert "Acme" in response.text and "Acme ERP" in response.text and "srv-01" in response.text
assert "erro curto" in response.text and "Job ID" in response.text and "Criado" in response.text
assert 'href="/jobs?job_id=' in response.text
assert "erro curto" in response.text and "Tipo / erro" in response.text and "Falhou" in response.text
assert 'href="/jobs?scope=active&amp;status=FAILED&amp;job_id=' in response.text
assert response.text.count("erro curto") == 10
assert 'title="2026-09-22T18:00:11+00:00"' in response.text
assert 'title="2026-09-22T18:05:11+00:00"' in response.text
assert ">22/09/2026 15:00<" in response.text
assert ">22/09/2026 15:05<" in response.text
with factory() as session:
session.query(Job).filter(Job.status == "FAILED").delete()
@@ -176,7 +236,7 @@ def test_agents_render_separate_health_and_fiscal_status_with_server_sort():
response = client.get("/agents?sort=worst_coverage&health=ONLINE&search=Acme")
assert response.status_code == 200
for label in ("Cliente", "Nome de exibição", "Hostname", "Saúde técnica", "Status fiscal", "Cobertura fiscal", "Versão"):
for label in ("Cliente / servidor", "Saúde", "Status fiscal", "Cobertura", "Produtos", "Versão", "Último contato"):
assert label in response.text
assert "Acme ERP" in response.text
assert "srv-01" in response.text
@@ -222,9 +282,8 @@ def test_agent_list_hides_technical_uuid_and_explains_remainder():
assert response.status_code == 200
assert technical_uuid not in response.text
assert "classificados" in response.text
assert "bloqueados com segurança" in response.text
assert "falhas técnicas" in response.text
assert "bloqueados" in response.text
assert "Não informada" in response.text
assert "<td>Desconhecida<small>Desconhecida</small></td>" not in response.text
@@ -254,7 +313,7 @@ def test_jobs_render_client_identity_duration_and_preserve_filters():
response = client.get("/jobs?status=RUNNING&search=Acme")
assert response.status_code == 200
for label in ("Cliente", "Nome de exibição", "Hostname", "Job ID", "Tipo", "Status", "Criado", "Finalizado", "Duração", "Erro"):
for label in ("Cliente / servidor", "Tipo / job", "Status", "Criado", "Finalizado / duração", "Erro / ação"):
assert label in response.text
assert "Acme" in response.text
assert "srv-01" in response.text
@@ -300,10 +359,10 @@ def test_blockers_render_visible_filters_summary_priority_and_detail_link():
response = client.get("/blockers?client=Acme&installation=srv-01&field=CFOP&blocker_type=BLOCKED_MISSING_EVIDENCE&status=OPEN&age=0h&search=P-001")
assert response.status_code == 200
for label in ("Cliente", "Instalação / Agent / Servidor", "Campo", "Tipo de blocker", "Status", "Idade", "Produto ou busca", "Blockers abertos", "Produtos afetados", "Clientes afetados", "CFOP", "ST", "ICMS", "CSOSN", "NCM", "OTHER", "Prioridade"):
for label in ("Cliente", "Instalação / servidor", "Campo", "Tipo de blocker", "Status", "Idade", "Produto ou busca", "Blockers abertos", "Produtos afetados", "Clientes afetados", "CFOP", "ST", "ICMS", "CSOSN", "NCM", "Outros", "Prioridade"):
assert label in response.text
assert f"/blockers/{blocker_id}" in response.text
assert "CRITICAL" in response.text or "HIGH" in response.text
assert "Alta" in response.text and "Evidência fiscal ausente" in response.text
assert 'value="Acme"' in response.text
assert 'value="BLOCKED_MISSING_EVIDENCE"' in response.text
assert "window.confirm" not in response.text