init: rapid_dashboards app from fn_registry

This commit is contained in:
dataforge
2026-04-06 00:57:13 +02:00
commit b7f354e081
46 changed files with 6139 additions and 0 deletions
+525
View File
@@ -0,0 +1,525 @@
# 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 }
```
+569
View File
@@ -0,0 +1,569 @@
# Rapid Dashboards — Guia de uso
Dashboard builder declarativo: defines un YAML, obtienes un dashboard desktop con datos en vivo.
## Inicio rapido
```bash
cd apps/rapid_dashboards
# Desarrollo — usa symlink dashboard.yaml (ver "Cambiar entre dashboards")
wails dev
# Desarrollo — build produccion
CGO_ENABLED=1 wails build -tags fts5
./build/bin/rapid-dashboards --dashboard examples/fn_registry_overview.yaml
```
## Compilar un dashboard especifico
### Desarrollo (wails dev)
`wails dev` no pasa argumentos CLI al binario. El binario busca el dashboard en este orden:
1. Flag `--dashboard <path>` (solo funciona con `wails build`, no con `wails dev`)
2. Variable de entorno `DASHBOARD` (no se propaga al binario en `wails dev`)
3. Archivo `dashboard.yaml` en el cwd (metodo recomendado para desarrollo)
**Para desarrollo, usar un symlink:**
```bash
# Apuntar al dashboard deseado
ln -sf examples/fn_registry_overview.yaml dashboard.yaml
# Lanzar — el binario encuentra dashboard.yaml automaticamente
wails dev
```
### Produccion (wails build)
```bash
# Compilar
CGO_ENABLED=1 wails build -tags fts5
# Ejecutar con un YAML especifico
./build/bin/rapid-dashboards --dashboard examples/fn_registry_overview.yaml
./build/bin/rapid-dashboards --dashboard /ruta/absoluta/mi_dashboard.yaml
```
El binario compilado acepta el flag `--dashboard` directamente.
---
## Cambiar entre dashboards
### En vivo (dropdown en la app)
Si hay mas de un archivo `.yaml` en el directorio del dashboard actual, aparece un dropdown junto al titulo. Al seleccionar otro dashboard:
- Se cargan las nuevas conexiones, queries y widgets
- Se cierran las conexiones anteriores
- El tema se aplica automaticamente
Los dashboards disponibles se escanean del directorio donde esta el YAML actual (por defecto `examples/`).
### Cambiando el symlink (para desarrollo)
```bash
# Ver dashboards disponibles
ls examples/*.yaml
# Cambiar al dashboard de apps
ln -sf examples/fn_registry_apps.yaml dashboard.yaml
# Cambiar al overview de funciones
ln -sf examples/fn_registry_overview.yaml dashboard.yaml
# Reiniciar wails dev para que tome el cambio
```
### Por CLI (produccion)
```bash
# Dashboard de funciones
./build/bin/rapid-dashboards --dashboard examples/fn_registry_overview.yaml
# Dashboard de apps
./build/bin/rapid-dashboards --dashboard examples/fn_registry_apps.yaml
```
---
## Temas
El campo `theme` del YAML controla toda la estetica del dashboard. Cada tema cambia colores de fondo, texto, cards, bordes, acentos y la paleta de colores de los graficos.
### Temas disponibles
| Tema | Descripcion | Uso recomendado |
|------|-------------|-----------------|
| `dark` | Azul-gris profundo, acentos azules | Default, dashboards tecnicos |
| `emerald` | Verde esmeralda oscuro, acentos dorados | Dashboards financieros, naturaleza |
| `amber` | Calido ambar/naranja sobre fondo oscuro | Dashboards de alertas, operaciones |
| `rose` | Rosa/magenta sobre fondo oscuro | Dashboards de marketing, analytics |
| `light` | Fondo claro con sombras sutiles | Presentaciones, pantallas con luz |
### Ejemplo de uso
```yaml
# Dashboard con tema esmeralda
theme: "emerald"
# Dashboard con tema claro
theme: "light"
```
### Que cambia cada tema
- **Background y foreground**: tonos base del fondo y texto
- **Card**: color de fondo de widgets, sin bordes en temas oscuros, con sombra en light
- **Primary/accent**: color de acento para interacciones y highlights
- **Border**: tonos de separadores y bordes
- **Chart palette**: cada tema tiene su propia secuencia de colores para graficos (`--chart-1` a `--chart-5`)
### Anadir un tema nuevo
Anadir un bloque `[data-theme="nombre"]` en `frontend/src/app.css` con todas las variables CSS. El tema se activa automaticamente al usar `theme: "nombre"` en el YAML.
---
## Estructura del YAML
Un dashboard se define con 5 bloques:
```yaml
settings: # titulo, dimensiones, refresh global, columnas
theme: # tema visual
connections: # bases de datos
queries: # SQL + refresh + params
filters: # controles de filtro
sections: # secciones con widgets
```
---
## 1. settings
```yaml
settings:
title: "Mi Dashboard" # titulo de la ventana
refresh: 30s # refresh global por defecto
width: 1280 # ancho ventana (px)
height: 800 # alto ventana (px)
columns: 12 # columnas del grid CSS
```
- `refresh` acepta duraciones Go: `200ms`, `1s`, `5s`, `1m`, `30m`
- `columns` define el grid base. Cada widget usa `span` para ocupar N columnas.
---
## 2. connections
Cada conexion tiene un nombre y un driver. Secrets via variables de entorno `${VAR}`.
### SQLite
```yaml
connections:
local:
driver: sqlite
path: ./data.db
```
### PostgreSQL
```yaml
connections:
main_db:
driver: postgres
host: localhost
port: 5432
user: analytics
password: "${PG_PASSWORD}"
database: myapp
sslmode: disable
```
### DuckDB
```yaml
connections:
warehouse:
driver: duckdb
path: ./warehouse.duckdb
```
### ClickHouse
```yaml
connections:
events:
driver: clickhouse
host: localhost
port: 9000
user: default
password: ""
database: events
```
**Paths relativos**: se resuelven desde el cwd del binario, NO desde la ubicacion del YAML. En desarrollo el cwd es `apps/rapid_dashboards/`, asi que `../../registry.db` apunta a la raiz del repo.
---
## 3. queries
```yaml
queries:
revenue_total:
connection: main_db
sql: "SELECT SUM(amount) as value FROM orders"
refresh: 10s
stale_time: 5s
revenue_by_date:
connection: main_db
sql: |
SELECT date, SUM(amount) as revenue
FROM orders
WHERE date >= :date_from AND date <= :date_to
ORDER BY date
params:
date_from: "$filter.date_range.from"
date_to: "$filter.date_range.to"
```
### Parametros
- `:nombre` en el SQL se reemplaza por el valor del param.
- `$filter.xxx` referencia el valor actual de un filtro.
- `$filter.date_range.from` accede al subcampo `from`.
- Placeholders se convierten al formato del driver (`$1` para Postgres, `?` para SQLite/DuckDB/ClickHouse).
### Fechas relativas
| Valor | Resultado |
|-------|-----------|
| `now` | timestamp actual |
| `now-7d` | hace 7 dias |
| `now-24h` | hace 24 horas |
| `now-30m` | hace 30 minutos |
---
## 4. filters
### Select
```yaml
filters:
category:
type: select
label: "Categoria"
default: "all"
options:
- { label: "Todas", value: "all" }
- { label: "Electronics", value: "electronics" }
```
### Date Range
```yaml
filters:
date_range:
type: date_range
label: "Periodo"
default: { from: "now-7d", to: "now" }
presets:
- { label: "7 dias", from: "now-7d", to: "now" }
- { label: "30 dias", from: "now-30d", to: "now" }
```
### Text
```yaml
filters:
search:
type: text
label: "Buscar"
placeholder: "Buscar por nombre..."
debounce: 300
```
---
## 5. sections y widgets
```yaml
sections:
- id: kpis
title: "Metricas"
columns: 4 # override de columnas
widgets:
- id: total_users
type: kpi
title: "Usuarios"
query: count_users
mapping: { value: "value" }
span: 1
- id: charts
title: "Graficos"
collapsible: true
columns: 2
widgets:
- id: revenue_line
type: line_chart
title: "Revenue"
query: revenue_by_date
mapping:
x: "date"
series:
- { key: "revenue", name: "Revenue", color: "#3b82f6" }
options:
zoomable: true
span: 1
```
---
## Tipos de widget
### kpi
```yaml
- type: kpi
mapping:
value: "campo_sql"
format: "$,.2f" # opcional
```
### line_chart
```yaml
- type: line_chart
mapping:
x: "date"
series:
- { key: "revenue", name: "Revenue", color: "#3b82f6" }
options:
curve: monotone # linear | monotone | step
show_grid: true
show_legend: true
zoomable: true
height: 400
```
### bar_chart
```yaml
- type: bar_chart
mapping:
x: "category"
y: "count"
# O multi-series con series: [...]
options:
horizontal: true # barras horizontales
show_grid: true
show_legend: true
height: 300
```
### pie_chart
```yaml
- type: pie_chart
mapping:
name: "domain" # campo para nombres de segmentos
value: "cantidad" # campo numerico
options:
donut: true # hueco central
show_legend: true # default: true
height: 300
```
### area_chart
```yaml
- type: area_chart
mapping:
x: "date"
series:
- { key: "users", name: "Usuarios" }
options:
stacked: true
height: 300
```
### sparkline
```yaml
- type: sparkline
mapping:
value: "metric"
options:
variant: area # line | area | bar
width: 200
height: 40
```
### table
```yaml
- type: table
mapping:
columns:
- { key: "name", label: "Nombre" }
- { key: "amount", label: "Monto", format: "$,.2f" }
- { key: "created_at", label: "Fecha", format: "datetime" }
options:
heatmap_columns: ["go", "python", "bash"] # colorea celdas por intensidad
```
Si no defines `columns`, se auto-detectan del resultado SQL.
**Heatmap**: `options.heatmap_columns` acepta un array de nombres de columna. Las celdas se colorean de azul oscuro (valor minimo) a azul brillante (valor maximo), calculado por columna.
---
## Formatos de valor
| Formato | Ejemplo | Resultado |
|---------|---------|-----------|
| `$,.2f` | 1234.5 | $1,234.50 |
| `,.2f` | 1234.5 | 1,234.50 |
| `.2f` | 1234.5 | 1234.50 |
| `,` | 1234 | 1,234 |
| `datetime` | ISO string | locale datetime |
---
## Variables de entorno
Cualquier campo del YAML soporta `${VARIABLE}`:
```bash
DB_HOST=prod.example.com DB_PASSWORD=secret \
./rapid-dashboards --dashboard prod.yaml
```
---
## Puntos clave (aprendizajes)
### Wails dev no pasa CLI args al binario
`wails dev -- --flag value` no funciona: Wails compila y ejecuta el binario como proceso hijo sin pasar los args despues de `--`. Tampoco propaga variables de entorno al binario. La solucion es usar un archivo `dashboard.yaml` en el cwd que el binario detecta automaticamente.
### Wails genera bindings ejecutando el binario sin args
Durante `wails dev`, el primer paso es generar bindings TypeScript. Para esto Wails ejecuta el binario sin argumentos. Si el binario requiere flags obligatorios y hace `os.Exit(1)`, la generacion falla. Solucion: detectar cuando no hay args y usar un config dummy con slices vacios (no nil) para que los tipos se generen correctamente.
### Slices nil de Go se serializan como null en JSON
`[]SectionDef(nil)` se serializa como `null`, no como `[]`. Si el frontend hace `.map()` sobre null, crash. Inicializar siempre con `[]SectionDef{}` para el config dummy, y usar `(config.sections ?? [])` en el frontend.
### Tipos de Wails vs tipos manuales de TypeScript
Wails genera `models.ts` con tipos `string` genericos. Si el frontend tiene tipos mas estrictos (union types como `"select" | "date_range"`), hay incompatibilidad. Solucion: castear los imports con `as unknown as typeof X`.
### Paths relativos se resuelven desde el cwd, no desde el YAML
`SQLiteOpen(path)` resuelve paths relativos al cwd del proceso. Si el YAML esta en `examples/` y el binario corre desde `apps/rapid_dashboards/`, un path `../../registry.db` es correcto pero `../../../registry.db` (relativo al YAML) no. Siempre pensar los paths desde el cwd del binario.
### Recharts Pie necesita valores numericos
SQLite puede devolver numeros como strings en algunos contextos. Recharts Pie no renderiza nada si `dataKey` apunta a un string. Solucion: coercer a `Number()` antes de pasar a Pie.
### Recharts PieLabelRenderProps es estricto
El tipo `label` de `<Pie>` no acepta callbacks con tipos genericos como `Record<string, unknown>`. Hay que importar `PieLabelRenderProps` de `recharts` y usar una funcion con nombre tipada, no un lambda inline.
### Animaciones de Recharts
Las animaciones por defecto de Recharts Pie son lentas en dashboards que refrescan frecuentemente. Usar `isAnimationActive={false}` para render instantaneo.
---
## Scripts de lanzamiento
Cada dashboard YAML debe tener forma de lanzarse rapido desde `scripts/`. La carpeta `scripts/` contiene dos scripts genericos que reciben el nombre del dashboard como argumento:
```bash
# Listar dashboards disponibles
./scripts/dev.sh
# Desarrollo — crea symlink + wails dev
./scripts/dev.sh fn_registry_overview
# Produccion — compila si es necesario + ejecuta
./scripts/prod.sh fn_registry_apps
# Produccion con variables de entorno
DB_PASSWORD=secret ./scripts/prod.sh mi_dashboard
```
### Convencion
Al crear un nuevo YAML en `examples/`, no hace falta crear scripts adicionales — `dev.sh` y `prod.sh` descubren automaticamente los `.yaml` de `examples/`. Solo ejecutar con el nombre (sin extension):
```bash
# Nuevo dashboard
vim examples/mi_nuevo_dashboard.yaml
# Lanzar inmediatamente
./scripts/dev.sh mi_nuevo_dashboard
```
`dev.sh` crea el symlink `dashboard.yaml` y lanza `wails dev`. `prod.sh` compila con `CGO_ENABLED=1 wails build -tags fts5` si el binario no existe o el YAML es mas reciente, y ejecuta con `--dashboard`.
---
## Compilacion multiplataforma
### Linux (default)
```bash
CGO_ENABLED=1 wails build -tags fts5
```
Genera `build/bin/rapid-dashboards` con soporte para SQLite, PostgreSQL, DuckDB y ClickHouse.
### Windows (cross-compile desde Linux)
```bash
CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 \
wails build -tags "fts5 noduckdb noclickhouse" -platform windows/amd64 -skipbindings
```
Genera `build/bin/rapid-dashboards.exe`. Requiere `mingw-w64` instalado (`apt install gcc-mingw-w64-x86-64`).
**Limitacion:** DuckDB y ClickHouse se excluyen con los tags `noduckdb` y `noclickhouse` porque `go-duckdb` depende de libstdc++ y no cross-compila bien desde Linux. El `.exe` solo soporta SQLite y PostgreSQL. Si un YAML usa esos drivers, dara error: `"duckdb support not compiled"`.
### Windows nativo (con DuckDB y ClickHouse)
Para compilar con todos los drivers, compilar directamente en Windows con Go y MinGW/MSYS2 instalados:
```powershell
# En Windows con Go + MinGW (MSYS2)
$env:CGO_ENABLED=1
wails build -tags fts5
```
Esto genera un `.exe` con soporte completo: SQLite, PostgreSQL, DuckDB y ClickHouse.
### Resumen de soporte por plataforma
| Driver | Linux | Windows (cross-compile) | Windows (nativo) |
|---|---|---|---|
| SQLite | si | si | si |
| PostgreSQL | si | si | si |
| DuckDB | si | no | si |
| ClickHouse | si | no | si |
### Rutas en Windows
Las rutas no se rompen entre plataformas. El codigo usa `filepath.Join` y `filepath.IsAbs` de Go, que adaptan los separadores automaticamente (`/` en Linux, `\` en Windows). Los paths relativos en el YAML (ej: `../../registry.db`) funcionan igual en ambos OS.
---
## Extensibilidad
Para anadir un tipo de widget nuevo:
1. Crear `frontend/src/components/widgets/MiWidget.tsx` implementando `WidgetProps`
2. Registrar en `frontend/src/components/widgets/register.ts`: `registerWidget('mi_tipo', MiWidget)`
3. Usar en YAML: `type: mi_tipo`
+202
View File
@@ -0,0 +1,202 @@
# Schema YAML — Referencia completa
Referencia rapida de todos los campos del YAML de dashboard.
## Top level
| Campo | Tipo | Requerido | Default | Descripcion |
|-------|------|-----------|---------|-------------|
| `settings` | object | si | — | Configuracion global |
| `theme` | string | no | `"dark"` | Nombre del tema |
| `connections` | map | si | — | Conexiones a bases de datos |
| `queries` | map | si | — | Definiciones de queries SQL |
| `filters` | map | no | `{}` | Filtros interactivos |
| `sections` | array | si | — | Secciones con widgets |
## settings
| Campo | Tipo | Default | Descripcion |
|-------|------|---------|-------------|
| `title` | string | — | Titulo de la ventana (requerido) |
| `refresh` | duration | `"30s"` | Refresh global por defecto |
| `width` | int | `1280` | Ancho de ventana en px |
| `height` | int | `800` | Alto de ventana en px |
| `columns` | int | `12` | Columnas del grid CSS |
## connections[nombre]
| Campo | Tipo | Drivers | Descripcion |
|-------|------|---------|-------------|
| `driver` | string | todos | `sqlite` \| `postgres` \| `duckdb` \| `clickhouse` |
| `path` | string | sqlite, duckdb | Ruta al archivo de BD |
| `host` | string | postgres, clickhouse | Hostname |
| `port` | int | postgres, clickhouse | Puerto (default: 5432/9000) |
| `user` | string | postgres, clickhouse | Usuario |
| `password` | string | postgres, clickhouse | Password (soporta `${ENV}`) |
| `database` | string | postgres, clickhouse | Nombre de la base de datos |
| `sslmode` | string | postgres | Modo SSL (default: `"disable"`) |
## queries[nombre]
| Campo | Tipo | Default | Descripcion |
|-------|------|---------|-------------|
| `connection` | string | — | Nombre de la conexion (requerido) |
| `sql` | string | — | Query SQL (requerido) |
| `refresh` | duration | global | Intervalo de re-ejecucion |
| `stale_time` | duration | refresh/2 | Tiempo antes de considerar dato viejo |
| `params` | map | `{}` | Parametros: `nombre: valor_o_$filter.ref` |
## filters[nombre]
| Campo | Tipo | Tipos de filtro | Descripcion |
|-------|------|-----------------|-------------|
| `type` | string | todos | `select` \| `date_range` \| `text` |
| `label` | string | todos | Etiqueta visible |
| `default` | any | todos | Valor por defecto |
| `options` | array | select | `[{ label, value }]` |
| `presets` | array | date_range | `[{ label, from, to }]` |
| `placeholder` | string | text | Placeholder del input |
| `debounce` | int | text | Delay en ms (default: 300) |
## sections[]
| Campo | Tipo | Default | Descripcion |
|-------|------|---------|-------------|
| `id` | string | — | Identificador unico (requerido) |
| `title` | string | — | Titulo visible |
| `collapsible` | bool | `false` | Permite colapsar |
| `columns` | int | global | Override de columnas del grid |
| `widgets` | array | — | Widgets de esta seccion |
## widgets[]
| Campo | Tipo | Default | Descripcion |
|-------|------|---------|-------------|
| `id` | string | — | Identificador unico (requerido) |
| `type` | string | — | Tipo de componente (requerido) |
| `title` | string | — | Titulo del widget |
| `query` | string | — | Nombre de la query (requerido) |
| `mapping` | object | — | Mapeo de campos SQL a props |
| `options` | object | `{}` | Opciones del componente |
| `span` | int | `1` | Columnas que ocupa |
| `row_span` | int | `1` | Filas que ocupa |
## Tipos de widget y sus mappings
### kpi
```
mapping.value → campo del resultado (string)
mapping.format → formato de visualizacion (string, opcional)
```
### line_chart / bar_chart / area_chart
```
mapping.x → campo del eje X (string)
mapping.y → campo del eje Y — single series (string)
mapping.series → multi-series: [{ key, name, color? }]
```
### sparkline
```
mapping.value → campo numerico (string)
```
### table
```
mapping.columns → [{ key, label, format? }]
Si no se define, auto-detecta del resultado.
```
## Options por tipo
### line_chart
| Option | Tipo | Default | Descripcion |
|--------|------|---------|-------------|
| `curve` | string | `"monotone"` | linear, monotone, step, stepBefore, stepAfter |
| `show_grid` | bool | `true` | Mostrar cuadricula |
| `show_legend` | bool | `false` | Mostrar leyenda |
| `zoomable` | bool | `false` | Habilitar brush zoom |
| `height` | int | `300` | Altura en px |
### bar_chart
| Option | Tipo | Default | Descripcion |
|--------|------|---------|-------------|
| `horizontal` | bool | `false` | Barras horizontales |
| `show_grid` | bool | `true` | Mostrar cuadricula |
| `show_legend` | bool | `false` | Mostrar leyenda |
| `height` | int | `300` | Altura en px |
### area_chart
| Option | Tipo | Default | Descripcion |
|--------|------|---------|-------------|
| `stacked` | bool | `false` | Apilar areas |
| `show_grid` | bool | `true` | Mostrar cuadricula |
| `show_legend` | bool | `false` | Mostrar leyenda |
| `height` | int | `300` | Altura en px |
### sparkline
| Option | Tipo | Default | Descripcion |
|--------|------|---------|-------------|
| `variant` | string | `"area"` | line, area, bar |
| `width` | int | `200` | Ancho en px |
| `height` | int | `40` | Altura en px |
## Duraciones validas
| Formato | Ejemplo |
|---------|---------|
| milisegundos | `100ms`, `200ms`, `500ms` |
| segundos | `1s`, `5s`, `30s` |
| minutos | `1m`, `5m`, `30m` |
| horas | `1h` |
| combinado | `1m30s` |
## Fechas relativas
| Formato | Resultado |
|---------|-----------|
| `now` | momento actual |
| `now-Ns` | hace N segundos |
| `now-Nm` | hace N minutos |
| `now-Nh` | hace N horas |
| `now-Nd` | hace N dias |
## KPI mappings extendidos (v2)
Ademas de `value` y `format`, el widget KPI soporta:
| Mapping | Tipo | Descripcion |
|---------|------|-------------|
| `value` | string | Campo SQL del valor principal |
| `format` | string | Formato: `$,.2f`, `,`, `datetime` |
| `unit` | string | Campo SQL para la unidad (ej: "k", "ms") |
| `unitLabel` | string | Unidad fija (alternativa a `unit` dinamico) |
| `delta` | string | Campo SQL para el cambio porcentual |
| `deltaLabel` | string | Texto antes del delta: "Increased by" |
| `deltaSuffix` | string | Texto despues del delta: "vs yesterday" |
| `sparkline` | string | Campo SQL con valores para mini barras |
| `sparklineColors` | string[] | Colores por barra |
Ejemplo:
```yaml
- type: kpi
mapping:
value: "total"
format: ","
unitLabel: "k"
delta: "pct_change"
deltaLabel: "Increased by"
deltaSuffix: "vs yesterday"
sparkline: "daily_value"
sparklineColors: ["#3b82f6", "#8b5cf6", "#f59e0b", "#10b981"]
```
## Scripts de lanzamiento
```bash
./scripts/dev.sh <nombre> # symlink + wails dev
./scripts/prod.sh <nombre> # build + ejecutar
```
Sin argumento lista los dashboards disponibles en `examples/`.