# 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 ` (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 `` no acepta callbacks con tipos genericos como `Record`. 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`