feat: UI polish - dark default, version drift vs default release, humanized enums, failed-jobs layout, processing button hierarchy, PT-BR titles/CTAs

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
gti
2026-09-24 14:26:44 -03:00
co-authored by Claude Sonnet 5
parent 9a142975a6
commit c950ffb47f
18 changed files with 247 additions and 52 deletions
+38 -4
View File
@@ -1,3 +1,4 @@
from pathlib import Path
import re
from datetime import datetime, timedelta, timezone
@@ -85,7 +86,7 @@ def test_dashboard_renders_operational_queue_labels_and_human_dates():
assert "Produtos com blockers abertos" in response.text
assert "Blockers abertos" in response.text
assert "Atenção necessária" in response.text
assert "Ver todos" in response.text
assert "Ver blockers" in response.text
assert "Jobs com falha" in response.text
assert "Agents desatualizados" in response.text
assert not re.search(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}", response.text)
@@ -188,9 +189,9 @@ 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 "Tipo / erro" in response.text and "Falhou" in response.text
assert "erro curto" in response.text and "Falhas recentes" 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 response.text.count('class="error-text job-error-summary"') == 10 # bounded, one clamped summary per row
assert 'title="2026-09-22T18:05:11+00:00"' in response.text
assert ">22/09/2026 15:05<" in response.text
with factory() as session:
@@ -458,7 +459,8 @@ def test_agent_detail_reconciles_coverage_and_formats_missing_technical_fields()
assert "Desconhecida · Desconhecida" not in response.text
assert "ODS -" not in response.text
context = response.text.split("<h2>Contexto da empresa</h2>", 1)[1].split("</article>", 1)[0]
assert "Reprocessar tudo" in context
assert "Reprocessar tudo" not in context # the only reprocess action lives in the Processamento panel
assert response.text.count("Reprocessar tudo") == 1
assert not re.search(r"<(?:a|button)\b[^>]*>\s*</(?:a|button)>", context)
@@ -502,3 +504,35 @@ def test_agent_event_filters_lookahead_and_safe_event_reference_links_and_confir
assert f'href="/agents/{agent_id}"' in confirmation.text
assert client.post(f"/agents/{agent_id}/reprocess", data={"csrf_token": "wrong", "confirm": "yes"}).status_code == 403
assert client.post(f"/agents/{agent_id}/reprocess", data={"csrf_token": csrf, "confirm": "yes"}, follow_redirects=False).status_code == 303
def test_theme_defaults_to_dark_and_never_overrides_a_saved_choice():
client, _, _ = _panel_client()
base = client.get("/agents").text
# no saved preference -> dark; an explicit "claro" is the only thing that yields light
assert 'tema === "claro" ? "claro" : "escuro"' in base
assert "prefers-color-scheme" not in (Path(__file__).parents[1] / "static" / "tema.js").read_text(encoding="utf-8")
script = (Path(__file__).parents[1] / "static" / "tema.js").read_text(encoding="utf-8")
assert 'return "escuro"' in script and 'localStorage.setItem("tema", novo)' in script
for page in ("login.html", "logged_out.html", "sso_error.html"):
assert 'data-tema="escuro"' in (Path(__file__).parents[1] / "templates" / page).read_text(encoding="utf-8")
def test_operator_pages_use_portuguese_titles_and_consistent_ctas():
client, agent_id, _ = _panel_client()
assert "<title>Agents / Servidores | GTI Fiscal</title>" in client.get("/agents").text
assert "<title>Blockers | GTI Fiscal</title>" in client.get("/blockers").text
assert "<title>Jobs | GTI Fiscal</title>" in client.get("/jobs").text
dashboard = client.get("/").text
for old in ("Abrir fila", "Ver instalações", "Ver todos", "Ver job #"):
assert old not in dashboard
def test_agent_detail_processing_hierarchy_and_status_badges():
client, agent_id, _ = _panel_client()
page = client.get(f"/agents/{agent_id}").text
assert page.count("Reprocessar tudo") == 1
assert 'class="primary-action"' in page and 'class="botao secondary"' in page and 'class="caution"' in page
assert "Mantém o heartbeat, mas não executa jobs" in page and "Recalcula todos os produtos" in page
assert "Saúde técnica: " not in page and "Status fiscal: " not in page # context lives in the small label, not in the badge
assert '<small class="status-label">Saúde técnica</small>' in page
@@ -564,3 +564,13 @@ def test_blocker_breakdown_query_groups_on_same_postgres_expression():
assert "GROUP BY blocked_products.field" in sql
assert "GROUP BY coalesce" not in sql
def test_remaining_technical_enums_have_human_labels():
from central.app.panel_presentation import health_reason_label
assert blocker_reason_label("INVALID_NCM") == "NCM inválido"
assert blocker_reason_label("LEGACY_NCM") == "NCM legado"
assert blocker_type_label("BLOCKED_MISSING_EVIDENCE") == "Evidência fiscal ausente"
assert health_reason_label("last heartbeat exceeds 300s") == "Sem heartbeat recente"
assert health_reason_label("persistent technical failure") == "Falha técnica persistente"
assert blocker_reason_label("SOMETHING_NEW") == "Something new" # unknown codes degrade readably, never crash
+63
View File
@@ -0,0 +1,63 @@
"""'Atualização disponível' must compare the Agent with the real default release, semantically."""
from datetime import datetime, timezone
from central.app.control_plane import dashboard_summary, list_installations
from central.app.installation_identity import compute_version_drift, outdated_versions, parse_version, reference_agent_version
from central.app.models import AgentRelease
from central.tests.test_control_plane import setup_db
from central.tests.test_operational_view_models import NOW, _installation
def _release(session, version, status="PUBLISHED", platform="windows"):
session.add(AgentRelease(version=version, platform=platform, architecture="x64", filename=f"GTIFiscalAgent-Setup-{version}.exe", storage_path=f"{version}.exe",
size_bytes=1, sha256=version.ljust(64, "0")[:64], status=status))
session.flush()
def test_versions_compare_semantically_not_as_strings():
assert compute_version_drift("1.1.7", "1.1.7") == "CURRENT"
assert compute_version_drift("1.1.10", "1.1.9") == "CURRENT" # string compare would say older
assert compute_version_drift("1.1.6", "1.1.7") == "OUTDATED"
assert compute_version_drift("1.2.0", "1.1.7") == "CURRENT" # ahead of the release is not "outdated"
assert compute_version_drift("1.0.0rc1", "1.0.0") == "OUTDATED" # pre-release sorts below its final
assert compute_version_drift(None, "1.1.7") == "UNKNOWN"
assert parse_version("garbage") is None
def test_reference_is_the_highest_published_release_and_ignores_revoked_and_drafts():
with setup_db()() as session:
_release(session, "1.1.5", "REVOKED")
_release(session, "1.1.6")
_release(session, "1.1.7")
_release(session, "1.9.9", "REVOKED")
_release(session, "2.0.0", "DRAFT")
_release(session, "3.0.0", platform="linux")
assert reference_agent_version(session) == "1.1.7"
def test_agent_on_the_default_release_is_current_everywhere_even_with_a_stale_snapshot():
with setup_db()() as session:
_release(session, "1.1.7")
_release(session, "1.1.6", "REVOKED")
# snapshot was written as OUTDATED by an older heartbeat (compared with a stale reference)
_installation(session, 1, version="1.1.7")
_installation(session, 2, version="1.1.6")
session.commit()
assert reference_agent_version(session) == "1.1.7"
assert outdated_versions(session, "1.1.7") == ["1.1.6"]
rows = {item["agent_version"]: item for item in list_installations(session, now=NOW)["items"]}
assert rows["1.1.7"]["version_drift"] == "CURRENT" and rows["1.1.6"]["version_drift"] == "OUTDATED"
assert [i["agent_version"] for i in list_installations(session, version="outdated", now=NOW)["items"]] == ["1.1.6"]
summary = dashboard_summary(session, now=NOW)
assert [a["agent_version"] for a in summary["outdated_agents"]] == ["1.1.6"]
def test_a_newer_published_release_makes_the_previous_one_outdated():
with setup_db()() as session:
_release(session, "1.1.7")
_installation(session, 1, version="1.1.7")
session.commit()
assert list_installations(session, version="outdated", now=NOW)["total"] == 0
_release(session, "1.1.8")
session.commit()
assert list_installations(session, version="outdated", now=NOW)["total"] == 1