# Charts

Widgets and saved queries carry a `chart_type` hint. Full list (matches the
frontend `ChartType` union in `frontend/src/types/index.ts`):

- `bar` — categorical comparison, **grouped** side-by-side bars. With many
  series (4+) over many categories each bar gets only a few pixels — for
  composition-over-time use `bar_stacked` instead.
- `bar_stacked` — one column per category, segments stacked. The right choice
  for multi-series composition (e.g. events per type per week).
- `bar_horizontal` — ranked categories with long labels.
- `line` — trend over time; multi-series supported.
- `area` / `area_stacked` — filled trend; stacked variant for composition.
- `combo` — bars + lines with optional right Y axis (`right_y_columns`).
- `pie` — share of a whole.
- `scatter` — two numeric axes.
- `funnel` — staged conversion.
- `treemap` — hierarchical share.
- `gauge` — single value against a max.
- `kpi` — a single headline number (with optional trend sparkline).
- `table` — raw rows (supports search via `enable_search`).

## Preview before committing — `bi_preview_chart`

Renders a chart preview (base64 PNG) from a saved query, so you can check the
shape before adding it to a dashboard:

```json
bi_preview_chart(
  chart_type="line",
  query_id=17,
  title="Solicitudes por mes",
  x_column="mes",
  y_columns=["solicitudes"]
)
```

- `query_id` points at an existing saved query.
- `x_column` is the category/time axis; `y_columns` are the measures.
- Omit `x_column`/`y_columns` to let the platform infer them from the result
  columns.

Once the preview looks right, persist `chart_type` on the saved query
(`bi_modify_query`) or set it directly when adding the widget
(`bi_add_widget`). To change a widget's axes/palette later, use
`bi_update_widget`.

## Widget style vs. query style

Dashboard widgets bound to a saved query inherit that query's visualization
style (chart type, palette, axes) on every dashboard load. Changing style via
`bi_update_widget` **locks** that widget's style (`style_locked`) so the
override sticks; changing it via `bi_modify_query` updates the query and
propagates to every *unlocked* widget bound to it. Prefer `bi_modify_query`
when the query has one widget or all its widgets should match.
