Files
2026-04-06 00:57:13 +02:00

526 lines
11 KiB
Markdown

# Ejemplos de dashboards
## 1. Dashboard minimo — una BD, un KPI
El dashboard mas simple posible: una conexion SQLite y un numero.
```yaml
settings:
title: "Contador"
refresh: 5s
columns: 4
theme: "dark"
connections:
db:
driver: sqlite
path: ./data.db
queries:
total:
sql: "SELECT COUNT(*) as value FROM items"
connection: db
filters: {}
sections:
- id: main
title: "Total"
widgets:
- id: count
type: kpi
title: "Items"
query: total
mapping: { value: "value" }
span: 4
```
---
## 2. Dashboard con filtros — Postgres + select + date range
Dashboard de ventas con filtros interactivos que re-ejecutan las queries.
```yaml
settings:
title: "Sales Dashboard"
refresh: 30s
columns: 12
theme: "dark"
connections:
sales:
driver: postgres
host: localhost
port: 5432
user: analytics
password: "${SALES_PW}"
database: sales
queries:
revenue_kpi:
connection: sales
sql: |
SELECT SUM(amount) as value, COUNT(*) as orders
FROM orders
WHERE created_at >= :from AND created_at <= :to
AND (:cat = 'all' OR category = :cat)
refresh: 15s
params:
from: "$filter.periodo.from"
to: "$filter.periodo.to"
cat: "$filter.categoria"
revenue_daily:
connection: sales
sql: |
SELECT date_trunc('day', created_at)::date as day,
SUM(amount) as revenue
FROM orders
WHERE created_at >= :from
AND (:cat = 'all' OR category = :cat)
GROUP BY day ORDER BY day
refresh: 30s
params:
from: "$filter.periodo.from"
cat: "$filter.categoria"
top_categories:
connection: sales
sql: |
SELECT category, SUM(amount) as revenue
FROM orders
WHERE created_at >= :from
GROUP BY category ORDER BY revenue DESC LIMIT 10
refresh: 30s
params:
from: "$filter.periodo.from"
recent_orders:
connection: sales
sql: |
SELECT id, customer, amount, category, status, created_at
FROM orders ORDER BY created_at DESC LIMIT 25
refresh: 10s
filters:
periodo:
type: date_range
label: "Periodo"
default: { from: "now-7d", to: "now" }
presets:
- { label: "Hoy", from: "now-0d", to: "now" }
- { label: "7d", from: "now-7d", to: "now" }
- { label: "30d", from: "now-30d", to: "now" }
- { label: "90d", from: "now-90d", to: "now" }
categoria:
type: select
label: "Categoria"
default: "all"
options:
- { label: "Todas", value: "all" }
- { label: "Electronics", value: "electronics" }
- { label: "Clothing", value: "clothing" }
- { label: "Food", value: "food" }
sections:
- id: kpis
title: "Resumen"
widgets:
- id: revenue
type: kpi
title: "Revenue"
query: revenue_kpi
mapping: { value: "value", format: "$,.2f" }
span: 4
- id: orders
type: kpi
title: "Ordenes"
query: revenue_kpi
mapping: { value: "orders", format: "," }
span: 4
- id: charts
title: "Tendencias"
columns: 2
widgets:
- id: revenue_line
type: line_chart
title: "Revenue diario"
query: revenue_daily
mapping:
x: "day"
series: [{ key: "revenue", name: "Revenue" }]
options: { curve: monotone, zoomable: true }
span: 1
- id: categories_bar
type: bar_chart
title: "Top categorias"
query: top_categories
mapping: { x: "category", y: "revenue" }
span: 1
- id: detail
title: "Ordenes recientes"
collapsible: true
widgets:
- id: orders_table
type: table
title: "Ultimas 25"
query: recent_orders
mapping:
columns:
- { key: "id", label: "ID" }
- { key: "customer", label: "Cliente" }
- { key: "amount", label: "Monto", format: "$,.2f" }
- { key: "category", label: "Categoria" }
- { key: "status", label: "Estado" }
- { key: "created_at", label: "Fecha", format: "datetime" }
span: 12
```
---
## 3. Dashboard multi-BD — SQLite + DuckDB
Combina datos de distintas bases en un solo dashboard.
```yaml
settings:
title: "Multi-DB Analytics"
refresh: 10s
columns: 12
theme: "dark"
connections:
ops:
driver: sqlite
path: ./operations.db
warehouse:
driver: duckdb
path: ./analytics.duckdb
queries:
entities_count:
connection: ops
sql: "SELECT COUNT(*) as value FROM entities"
refresh: 5s
executions_count:
connection: ops
sql: "SELECT COUNT(*) as value FROM executions"
refresh: 5s
assertions_pass_rate:
connection: ops
sql: |
SELECT
ROUND(100.0 * SUM(CASE WHEN passed = 1 THEN 1 ELSE 0 END) / COUNT(*), 1) as value
FROM assertion_results
refresh: 10s
daily_events:
connection: warehouse
sql: |
SELECT date_trunc('day', ts) as day, COUNT(*) as events
FROM events GROUP BY day ORDER BY day DESC LIMIT 30
refresh: 30s
status_distribution:
connection: ops
sql: "SELECT status, COUNT(*) as count FROM entities GROUP BY status"
refresh: 10s
filters: {}
sections:
- id: kpis
title: "Operations"
widgets:
- id: entities
type: kpi
title: "Entities"
query: entities_count
mapping: { value: "value" }
span: 4
- id: executions
type: kpi
title: "Executions"
query: executions_count
mapping: { value: "value" }
span: 4
- id: pass_rate
type: kpi
title: "Assertion Pass %"
query: assertions_pass_rate
mapping: { value: "value" }
span: 4
- id: charts
title: "Visualizacion"
columns: 2
widgets:
- id: events_area
type: area_chart
title: "Eventos diarios (DuckDB)"
query: daily_events
mapping:
x: "day"
series: [{ key: "events", name: "Eventos" }]
options: { show_grid: true }
span: 1
- id: status_bar
type: bar_chart
title: "Distribucion por status (SQLite)"
query: status_distribution
mapping: { x: "status", y: "count" }
span: 1
```
---
## 4. Dashboard tiempo real — refresh sub-segundo
Para metricas que cambian rapido (CPU, colas, precios).
```yaml
settings:
title: "Realtime Monitor"
refresh: 1s
columns: 12
theme: "dark"
connections:
metrics:
driver: sqlite
path: ./metrics.db
queries:
cpu_current:
connection: metrics
sql: "SELECT value FROM metrics WHERE key = 'cpu' ORDER BY ts DESC LIMIT 1"
refresh: 200ms
stale_time: 100ms
memory_current:
connection: metrics
sql: "SELECT value FROM metrics WHERE key = 'memory' ORDER BY ts DESC LIMIT 1"
refresh: 500ms
stale_time: 250ms
cpu_history:
connection: metrics
sql: |
SELECT ts, value FROM metrics
WHERE key = 'cpu' ORDER BY ts DESC LIMIT 60
refresh: 1s
queue_depth:
connection: metrics
sql: "SELECT value FROM metrics WHERE key = 'queue' ORDER BY ts DESC LIMIT 1"
refresh: 300ms
stale_time: 150ms
filters: {}
sections:
- id: live
title: "Live"
widgets:
- id: cpu
type: kpi
title: "CPU %"
query: cpu_current
mapping: { value: "value" }
span: 4
- id: mem
type: kpi
title: "Memory %"
query: memory_current
mapping: { value: "value" }
span: 4
- id: queue
type: kpi
title: "Queue"
query: queue_depth
mapping: { value: "value" }
span: 4
- id: history
title: "CPU History (60s)"
widgets:
- id: cpu_line
type: line_chart
title: "CPU % (ultimo minuto)"
query: cpu_history
mapping:
x: "ts"
y: "value"
options: { curve: linear, show_grid: true, height: 250 }
span: 12
```
---
## 5. Dashboard con busqueda — filtro texto
Dashboard que permite buscar texto con debounce.
```yaml
settings:
title: "Buscador de funciones"
refresh: 10s
columns: 12
theme: "dark"
connections:
registry:
driver: sqlite
path: ../../../registry.db
queries:
search_results:
connection: registry
sql: |
SELECT id, kind, lang, domain, description
FROM functions
WHERE id LIKE '%' || :q || '%'
OR description LIKE '%' || :q || '%'
ORDER BY updated_at DESC
LIMIT 50
refresh: 10s
params:
q: "$filter.busqueda"
result_count:
connection: registry
sql: |
SELECT COUNT(*) as value
FROM functions
WHERE id LIKE '%' || :q || '%'
OR description LIKE '%' || :q || '%'
refresh: 10s
params:
q: "$filter.busqueda"
filters:
busqueda:
type: text
label: "Buscar"
default: ""
placeholder: "nombre o descripcion..."
debounce: 300
sections:
- id: results
title: "Resultados"
widgets:
- id: count
type: kpi
title: "Coincidencias"
query: result_count
mapping: { value: "value" }
span: 3
- id: results_table
type: table
title: "Funciones encontradas"
query: search_results
mapping:
columns:
- { key: "id", label: "ID" }
- { key: "kind", label: "Kind" }
- { key: "lang", label: "Lang" }
- { key: "domain", label: "Domain" }
- { key: "description", label: "Descripcion" }
span: 12
```
---
## Patrones comunes
### Una query alimenta varios widgets
```yaml
queries:
summary:
sql: "SELECT SUM(amount) as revenue, COUNT(*) as orders, AVG(amount) as avg FROM orders"
sections:
- id: kpis
title: "KPIs"
widgets:
- id: rev
type: kpi
query: summary
mapping: { value: "revenue", format: "$,.2f" }
span: 4
- id: ord
type: kpi
query: summary
mapping: { value: "orders", format: "," }
span: 4
- id: avg
type: kpi
query: summary
mapping: { value: "avg", format: "$,.2f" }
span: 4
```
La query se ejecuta una sola vez y los 3 KPIs leen del mismo resultado.
### Seccion colapsable para detalle
```yaml
sections:
- id: detail
title: "Detalle (click para expandir)"
collapsible: true
widgets:
- id: log_table
type: table
query: logs
span: 12
```
### Grid flexible por seccion
```yaml
sections:
- id: kpis
title: "KPIs"
# hereda columns: 12 del global → 4 KPIs de span 3
widgets:
- { id: a, type: kpi, query: q, mapping: { value: v }, span: 3 }
- { id: b, type: kpi, query: q, mapping: { value: v }, span: 3 }
- { id: c, type: kpi, query: q, mapping: { value: v }, span: 3 }
- { id: d, type: kpi, query: q, mapping: { value: v }, span: 3 }
- id: charts
title: "Graficos"
columns: 2 # override: solo 2 columnas
widgets:
- { id: chart1, type: line_chart, query: q1, mapping: { x: a, y: b }, span: 1 }
- { id: chart2, type: bar_chart, query: q2, mapping: { x: a, y: b }, span: 1 }
- id: full
title: "Tabla"
columns: 1 # 1 columna = ancho completo
widgets:
- { id: tbl, type: table, query: q3, span: 1 }
```