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

570 lines
15 KiB
Markdown

# 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`