Release v4928.1.4.2 stable
This commit is contained in:
116
app/admin_ui/components.py
Normal file
116
app/admin_ui/components.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""Reusable HTML helpers for the admin UI refactor.
|
||||
|
||||
Keep these helpers dependency-light so pages can migrate from
|
||||
`admin_dashboard.py` gradually.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import html
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
|
||||
def esc(value: Any) -> str:
|
||||
return html.escape("" if value is None else str(value))
|
||||
|
||||
|
||||
def money(value: Any, currency: str = "EUR") -> str:
|
||||
try:
|
||||
amount = Decimal(str(value or "0"))
|
||||
return f"{amount:,.2f} {currency}".replace(",", "X").replace(".", ",").replace("X", ".")
|
||||
except Exception:
|
||||
return f"{esc(value)} {esc(currency)}"
|
||||
|
||||
|
||||
def fmt_dt(value: Any) -> str:
|
||||
if not value:
|
||||
return "—"
|
||||
if isinstance(value, datetime):
|
||||
return value.strftime("%Y-%m-%d %H:%M")
|
||||
return esc(value)
|
||||
|
||||
|
||||
def status_chip(status: str | None) -> str:
|
||||
status = (status or "unknown").lower()
|
||||
css = {
|
||||
"pending": "cf-chip-orange",
|
||||
"processing": "cf-chip-purple",
|
||||
"sent": "cf-chip-green",
|
||||
"failed": "cf-chip-red",
|
||||
"blocked": "cf-chip-red",
|
||||
"dry_run": "cf-chip-gray",
|
||||
"ignored": "cf-chip-gray",
|
||||
"cancelled": "cf-chip-gray",
|
||||
"created": "cf-chip-blue",
|
||||
"issued": "cf-chip-green",
|
||||
"converted": "cf-chip-green",
|
||||
}.get(status, "cf-chip-gray")
|
||||
return f'<span class="cf-chip {css}">{esc(status)}</span>'
|
||||
|
||||
|
||||
def kpi_icon_for(label: str) -> str:
|
||||
text = (label or "").lower()
|
||||
if "oportun" in text or "pipeline" in text:
|
||||
return "bi-funnel"
|
||||
if "valor" in text or "pagamento" in text or "finance" in text or "fatura" in text:
|
||||
return "bi-currency-euro"
|
||||
if "document" in text or "orçamento" in text or "orcamento" in text:
|
||||
return "bi-file-earmark-text"
|
||||
if "task" in text or "tarefa" in text or "pendente" in text:
|
||||
return "bi-list-check"
|
||||
if "email" in text or "comunica" in text or ("cliente" in text and "sem" in text):
|
||||
return "bi-envelope"
|
||||
if "erro" in text or "invál" in text or "inval" in text or "bloque" in text:
|
||||
return "bi-exclamation-triangle"
|
||||
if "envio" in text or "packlink" in text or "opera" in text or "prepar" in text:
|
||||
return "bi-truck"
|
||||
if "cliente" in text:
|
||||
return "bi-person-vcard"
|
||||
if "produto" in text or "artigo" in text or "categoria" in text or "jasmin" in text:
|
||||
return "bi-box-seam"
|
||||
if "integra" in text or "sistema" in text or "ambiente" in text or "estado" in text:
|
||||
return "bi-puzzle"
|
||||
if "retry" in text or "retries" in text:
|
||||
return "bi-arrow-clockwise"
|
||||
return "bi-grid"
|
||||
|
||||
|
||||
def kpi_tone_for(label: str, hint: str = "") -> str:
|
||||
text = f"{label or ''} {hint or ''}".lower()
|
||||
if "erro" in text or "falh" in text or "invál" in text or "inval" in text:
|
||||
return "cf-kpi-tone-red"
|
||||
if "pagamento" in text or "valor" in text or "finance" in text or "€" in text:
|
||||
return "cf-kpi-tone-green"
|
||||
if "envio" in text or "packlink" in text or "pendente" in text or "aguarda" in text or "incompleto" in text or "bloque" in text:
|
||||
return "cf-kpi-tone-orange"
|
||||
if "cliente" in text or "comunica" in text or "email" in text:
|
||||
return "cf-kpi-tone-purple"
|
||||
return ""
|
||||
|
||||
|
||||
def kpi_card(label: str, value: object, href: str | None = None, hint: str = "", icon: str | None = None, tone: str | None = None) -> str:
|
||||
tag = "a" if href else "div"
|
||||
href_attr = f' href="{esc(href)}"' if href else ""
|
||||
reset_class = " text-reset text-decoration-none" if href else ""
|
||||
icon_class = icon or kpi_icon_for(label)
|
||||
tone_class = tone or kpi_tone_for(label, hint)
|
||||
return f"""
|
||||
<{tag} class="cf-card cf-kpi{reset_class} {esc(tone_class)}"{href_attr}>
|
||||
<span class="cf-kpi-icon" aria-hidden="true"><i class="bi {esc(icon_class)}"></i></span>
|
||||
<span>{esc(label)}</span>
|
||||
<strong>{esc(value)}</strong>
|
||||
<small>{esc(hint)}</small>
|
||||
</{tag}>
|
||||
"""
|
||||
|
||||
|
||||
def alert_box(title: str, detail: str, tone: str = "warning") -> str:
|
||||
css = {
|
||||
"danger": "alert-danger",
|
||||
"warning": "alert-warning",
|
||||
"info": "alert-info",
|
||||
"success": "alert-success",
|
||||
}.get(tone, "alert-warning")
|
||||
return f'<div class="alert {css} border-0"><strong>{esc(title)}</strong><div>{esc(detail)}</div></div>'
|
||||
|
||||
Reference in New Issue
Block a user