"""Dashboard and landing routes. Moved from app.admin_dashboard in v4.7.2. The handlers still reuse legacy helpers to keep this refactor behavior-preserving. """ from fastapi import APIRouter import app.admin_dashboard as legacy from app.admin_dashboard import * # noqa: F401,F403 router = APIRouter() @router.get("/", response_class=HTMLResponse) async def admin_home(): """v4.5 clean Dashboard: visibility, not daily execution.""" metrics = get_admin_dashboard_metrics() ops = get_operations_summary(limit=6) counts = ops.get("counts") or {} comms = get_communications_summary() def n(key: str) -> int: return int(metrics.get(key) or counts.get(key) or 0) dashboard_cards = [ ("Oportunidades abertas", counts.get("open_opportunities", 0), "/opportunities?status=open", "Negócio em acompanhamento"), ("Valor / documentos", counts.get("open_quotations", 0), "/finance", "Orçamentos abertos"), ("Tasks pendentes", n("pending_total"), "/operations", "Trabalho humano por resolver"), ("Mensagens a rever", comms.get("open", 0), "/operations", "Ações vindas do Chatwoot"), ("Erros de integração", counts.get("outbox_failed", 0), "/outbox?status=failed", "Jasmin/Packlink/outbox"), ("Pagamentos por confirmar", n("pending_financeiro"), "/operations", "Fila financeira"), ("Envios pendentes", counts.get("shipments_pending", 0), "/orders", "Logística/Packlink"), ("Clientes incompletos", counts.get("customers_incomplete", 0), "/customers", "Dados fiscais/morada"), ] cards_html = "" for label, value, href, hint in dashboard_cards: cards_html += kpi_card(label, value, href, hint) alert_items = [] if int(counts.get("outbox_failed") or 0): alert_items.append(("Erro de integração", f"{counts.get('outbox_failed')} ação(ões) falhadas na outbox", "/outbox?status=failed", "cf-chip-red")) if int(comms.get("needs_review") or 0): alert_items.append(("Rever comunicação", f"{comms.get('needs_review')} mensagem(ns) com baixa confiança", "/operations", "cf-chip-orange")) if int(counts.get("customers_incomplete") or 0): alert_items.append(("Dados incompletos", f"{counts.get('customers_incomplete')} cliente(s) sem dados fiscais/morada completos", "/customers", "cf-chip-orange")) if int(counts.get("products_missing_jasmin") or 0): alert_items.append(("Produto bloqueante", f"{counts.get('products_missing_jasmin')} produto(s) ativos sem Artigo Jasmin", "/products?active=missing_jasmin", "cf-chip-red")) alert_html = "" for title, detail, href, chip in alert_items[:5]: alert_html += f"""
{esc(title)}
{esc(detail)}
Abrir →
""" if not alert_html: alert_html = '
Sem alertas críticos neste momento.
' body = f"""
Dashboard = visibilidade. O Chatwoot é a inbox. O ClientFlow mostra o trabalho, bloqueios e próximas ações.
Abrir Centro de trabalho
{cards_html}

Alertas

Sinais globais que merecem atenção.
Resolver no Centro de trabalho
{alert_html}

Modelo operacional v4.5

Dashboard
Mostra o estado e gargalos.
Centro de trabalho
Organiza o que precisa de ação agora.
Chatwoot → ClientFlow
O Chatwoot continua a ser a inbox; o ClientFlow transforma mensagens em ações, tasks e timeline.
Oportunidade
Mantém contexto, documentos, tasks, outbox e timeline.
""" return layout("Dashboard", "Visão geral do negócio e do sistema", body, "overview")