feat: add blocker resolution action to panel

This commit is contained in:
gti
2026-09-22 15:38:55 -03:00
parent 301aac112e
commit 66b5888d80
2 changed files with 20 additions and 2 deletions
+19 -1
View File
@@ -109,7 +109,25 @@ def router(session_factory):
def blockers(request: Request, page: int = 1, blocker_type: str | None = None):
session, auth_data, redirect = page_auth(request)
if redirect: return redirect
try: return render(request, "blockers.html", {"result": list_blockers(session, page=page, page_size=50, blocker_type=blocker_type), "auth": auth_data})
try: return render(request, "blockers.html", {"result": list_blockers(session, page=page, page_size=50, blocker_type=blocker_type), "auth": auth_data, "csrf_token": request.cookies.get("gti_csrf", "")})
finally: session.close()
@routes.post("/blockers/{blocker_id}/resolve")
async def resolve_blocker(request: Request, blocker_id: int):
session, auth_data, redirect = page_auth(request)
if redirect: return redirect
try:
values = await form_values(request); web_session, admin = auth_data
require_csrf(web_session, values.get("csrf_token"))
if values.get("confirm") != "yes" or not values.get("context", "").strip(): return HTMLResponse("confirmation and context required", status_code=400)
from .models import BlockedProduct
blocker = session.get(BlockedProduct, blocker_id)
if not blocker: return HTMLResponse("Blocker not found", status_code=404)
from datetime import datetime, timezone
blocker.resolved_at = datetime.now(timezone.utc); blocker.resolution_source = "HUMAN_PANEL"
record_audit(session, admin_user_id=admin.id, tenant_id=blocker.tenant_id, action="RESOLVE_BLOCKER", target_type="blocker", target_id=str(blocker_id), context={"context": values["context"]})
session.commit()
return RedirectResponse("/blockers", status_code=303)
finally: session.close()
@routes.get("/jobs", response_class=HTMLResponse)
+1 -1
View File
@@ -1 +1 @@
{% extends "base.html" %}{% block title %}Blockers | GTI Fiscal{% endblock %}{% block content %}<div class="page-heading"><div><p class="eyebrow">FILA FISCAL</p><h1>Blockers globais</h1></div></div><div class="panel table-wrap"><table><thead><tr><th>Cliente</th><th>Produto</th><th>Campo</th><th>Tipo</th><th>Motivo</th><th>Última ocorrência</th><th>Retries</th></tr></thead><tbody>{% for item in result.items %}<tr><td>{{ item.client or item.tenant_id }}</td><td><b>{{ item.product_code }}</b><small>{{ item.description or "" }}</small></td><td>{{ item.field }}</td><td><span class="badge blocked">{{ item.blocker_type }}</span></td><td>{{ item.reason }}</td><td>{{ item.last_seen_at }}</td><td>{{ item.retry_count }}</td></tr>{% else %}<tr><td colspan="7" class="muted">Nenhum blocker aberto.</td></tr>{% endfor %}</tbody></table></div>{% endblock %}
{% extends "base.html" %}{% block title %}Blockers | GTI Fiscal{% endblock %}{% block content %}<div class="page-heading"><div><p class="eyebrow">FILA FISCAL</p><h1>Blockers globais</h1></div></div><div class="panel table-wrap"><table><thead><tr><th>Cliente</th><th>Produto</th><th>Campo</th><th>Tipo</th><th>Motivo</th><th>Última ocorrência</th><th>Ação</th></tr></thead><tbody>{% for item in result.items %}<tr><td>{{ item.client or item.tenant_id }}</td><td><b>{{ item.product_code }}</b><small>{{ item.description or "" }}</small></td><td>{{ item.field }}</td><td><span class="badge blocked">{{ item.blocker_type }}</span></td><td>{{ item.reason }}</td><td>{{ item.last_seen_at }}<small>{{ item.retry_count }} retries</small></td><td><form method="post" action="/blockers/{{ item.id }}/resolve" data-confirm="Resolver este blocker com contexto explícito?"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><input type="hidden" name="confirm" value="yes"><input type="hidden" name="context" value="Resolved after operator review"><button class="secondary">Resolver</button></form></td></tr>{% else %}<tr><td colspan="7" class="muted">Nenhum blocker aberto.</td></tr>{% endfor %}</tbody></table></div>{% endblock %}