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:
@@ -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&health=ONLINE"' in home
|
||||
assert 'href="/blockers?scope=active&status=OPEN&field=NCM"' in home
|
||||
assert 'href="/jobs?scope=active&status=FAILED"' in home
|
||||
assert 'href="/agents?scope=active&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&status=FAILED&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
|
||||
|
||||
Reference in New Issue
Block a user