# `Lotus.Dashboards`
[🔗](https://github.com/typhoonworks/lotus/blob/v0.16.6/lib/lotus/dashboards.ex#L1)

Service functions for managing dashboards in Lotus.

Provides CRUD operations for dashboards, cards, filters, and filter mappings,
as well as execution functions for running all cards in a dashboard.

## Dashboard Structure

Dashboards contain:
- **Cards** - Query results, text, links, or headings arranged in a 12-column grid
- **Filters** - User inputs that control query variables across multiple cards
- **Filter Mappings** - Connections between dashboard filters and card query variables

## Execution

Use `run_dashboard/2` to execute all query cards in a dashboard simultaneously.
Filter values are resolved and passed to each card's query variables via the
configured mappings.

# `attrs`

```elixir
@type attrs() :: map()
```

# `id`

```elixir
@type id() :: integer() | binary()
```

# `create_dashboard`

```elixir
@spec create_dashboard(attrs()) ::
  {:ok, Lotus.Storage.Dashboard.t()} | {:error, Ecto.Changeset.t()}
```

Creates a new dashboard.

## Examples

    iex> create_dashboard(%{name: "Sales Dashboard"})
    {:ok, %Dashboard{}}

    iex> create_dashboard(%{})
    {:error, %Ecto.Changeset{}}

# `create_dashboard_card`

```elixir
@spec create_dashboard_card(Lotus.Storage.Dashboard.t() | id(), attrs()) ::
  {:ok, Lotus.Storage.DashboardCard.t()} | {:error, Ecto.Changeset.t()}
```

Creates a new card for a dashboard.

## Examples

    iex> create_dashboard_card(dashboard, %{
    ...>   card_type: :query,
    ...>   query_id: 123,
    ...>   position: 0,
    ...>   layout: %{x: 0, y: 0, w: 6, h: 4}
    ...> })
    {:ok, %DashboardCard{}}

# `create_dashboard_filter`

```elixir
@spec create_dashboard_filter(Lotus.Storage.Dashboard.t() | id(), attrs()) ::
  {:ok, Lotus.Storage.DashboardFilter.t()} | {:error, Ecto.Changeset.t()}
```

Creates a new filter for a dashboard.

## Examples

    iex> create_dashboard_filter(dashboard, %{
    ...>   name: "date_range",
    ...>   label: "Date Range",
    ...>   filter_type: :date_range,
    ...>   widget: :date_range_picker,
    ...>   position: 0
    ...> })
    {:ok, %DashboardFilter{}}

# `create_filter_mapping`

```elixir
@spec create_filter_mapping(
  Lotus.Storage.DashboardCard.t() | id(),
  Lotus.Storage.DashboardFilter.t() | id(),
  String.t(),
  keyword()
) ::
  {:ok, Lotus.Storage.DashboardCardFilterMapping.t()}
  | {:error, Ecto.Changeset.t()}
```

Creates a filter mapping connecting a dashboard filter to a card's query variable.

## Options

  * `:transform` - Optional transformation config for the filter value

## Examples

    iex> create_filter_mapping(card, filter, "start_date")
    {:ok, %DashboardCardFilterMapping{}}

    iex> create_filter_mapping(card, filter, "end_date", transform: %{type: "date_range_end"})
    {:ok, %DashboardCardFilterMapping{}}

# `delete_dashboard`

```elixir
@spec delete_dashboard(Lotus.Storage.Dashboard.t()) ::
  {:ok, Lotus.Storage.Dashboard.t()} | {:error, Ecto.Changeset.t()}
```

Deletes a dashboard.

Also deletes all associated cards, filters, and filter mappings.

## Examples

    iex> delete_dashboard(dashboard)
    {:ok, %Dashboard{}}

# `delete_dashboard_card`

```elixir
@spec delete_dashboard_card(Lotus.Storage.DashboardCard.t() | id()) ::
  {:ok, Lotus.Storage.DashboardCard.t()}
  | {:error, Ecto.Changeset.t() | :not_found}
```

Deletes a card.

Also deletes all associated filter mappings.

# `delete_dashboard_filter`

```elixir
@spec delete_dashboard_filter(Lotus.Storage.DashboardFilter.t() | id()) ::
  {:ok, Lotus.Storage.DashboardFilter.t()}
  | {:error, Ecto.Changeset.t() | :not_found}
```

Deletes a filter.

Also deletes all associated filter mappings.

# `delete_filter_mapping`

```elixir
@spec delete_filter_mapping(Lotus.Storage.DashboardCardFilterMapping.t() | id()) ::
  {:ok, Lotus.Storage.DashboardCardFilterMapping.t()}
  | {:error, Ecto.Changeset.t() | :not_found}
```

Deletes a filter mapping.

# `disable_public_sharing`

```elixir
@spec disable_public_sharing(Lotus.Storage.Dashboard.t()) ::
  {:ok, Lotus.Storage.Dashboard.t()} | {:error, Ecto.Changeset.t()}
```

Disables public sharing for a dashboard by removing its token.

## Examples

    iex> disable_public_sharing(dashboard)
    {:ok, %Dashboard{public_token: nil}}

# `enable_public_sharing`

```elixir
@spec enable_public_sharing(Lotus.Storage.Dashboard.t()) ::
  {:ok, Lotus.Storage.Dashboard.t()} | {:error, Ecto.Changeset.t()}
```

Enables public sharing for a dashboard by generating a unique token.

The token can be used to access the dashboard without authentication
via `get_dashboard_by_token/1`.

## Examples

    iex> enable_public_sharing(dashboard)
    {:ok, %Dashboard{public_token: "abc123..."}}

# `get_dashboard`

```elixir
@spec get_dashboard(id()) :: Lotus.Storage.Dashboard.t() | nil
```

Gets a single dashboard by ID.

Returns `nil` if the dashboard does not exist.

# `get_dashboard!`

```elixir
@spec get_dashboard!(id()) :: Lotus.Storage.Dashboard.t() | no_return()
```

Gets a single dashboard by ID.

Raises `Ecto.NoResultsError` if the dashboard does not exist.

# `get_dashboard_by_token`

```elixir
@spec get_dashboard_by_token(String.t()) :: Lotus.Storage.Dashboard.t() | nil
```

Gets a dashboard by its public sharing token.

Returns `nil` if no dashboard has the given token.

# `get_dashboard_card`

```elixir
@spec get_dashboard_card(
  id(),
  keyword()
) :: Lotus.Storage.DashboardCard.t() | nil
```

Gets a single card by ID.

Returns `nil` if the card does not exist.

## Options

  * `:preload` - A list of associations to preload

# `get_dashboard_card!`

```elixir
@spec get_dashboard_card!(
  id(),
  keyword()
) :: Lotus.Storage.DashboardCard.t() | no_return()
```

Gets a single card by ID.

Raises `Ecto.NoResultsError` if the card does not exist.

## Options

  * `:preload` - A list of associations to preload

# `get_dashboard_filter`

```elixir
@spec get_dashboard_filter(id()) :: Lotus.Storage.DashboardFilter.t() | nil
```

Gets a single filter by ID.

Returns `nil` if the filter does not exist.

# `get_dashboard_filter!`

```elixir
@spec get_dashboard_filter!(id()) :: Lotus.Storage.DashboardFilter.t() | no_return()
```

Gets a single filter by ID.

Raises `Ecto.NoResultsError` if the filter does not exist.

# `list_card_filter_mappings`

```elixir
@spec list_card_filter_mappings(Lotus.Storage.DashboardCard.t() | id()) :: [
  Lotus.Storage.DashboardCardFilterMapping.t()
]
```

Lists all filter mappings for a card.

# `list_dashboard_cards`

```elixir
@spec list_dashboard_cards(
  Lotus.Storage.Dashboard.t() | id(),
  keyword()
) :: [Lotus.Storage.DashboardCard.t()]
```

Lists all cards for a dashboard.

Returns cards ordered by position, then by id.

## Options

  * `:preload` - A list of associations to preload (e.g., `[:query, :filter_mappings]`)

## Examples

    iex> list_dashboard_cards(dashboard)
    [%DashboardCard{}, ...]

    iex> list_dashboard_cards(dashboard_id, preload: [:query, :filter_mappings])
    [%DashboardCard{query: %Query{}, filter_mappings: [...]}, ...]

# `list_dashboard_filters`

```elixir
@spec list_dashboard_filters(Lotus.Storage.Dashboard.t() | id()) :: [
  Lotus.Storage.DashboardFilter.t()
]
```

Lists all filters for a dashboard.

Returns filters ordered by position, then by id.

# `list_dashboards`

```elixir
@spec list_dashboards() :: [Lotus.Storage.Dashboard.t()]
```

Lists all dashboards.

Returns dashboards ordered by name.

# `list_dashboards_by`

```elixir
@spec list_dashboards_by(keyword()) :: [Lotus.Storage.Dashboard.t()]
```

Lists dashboards with optional filtering.

## Options

  * `:search` - Search term to match against dashboard names (case insensitive)

## Examples

    iex> list_dashboards_by(search: "sales")
    [%Dashboard{name: "Sales Overview"}, ...]

# `reorder_dashboard_cards`

```elixir
@spec reorder_dashboard_cards(Lotus.Storage.Dashboard.t() | id(), [id()]) :: :ok
```

Reorders cards in a dashboard.

Accepts a list of card IDs in the desired order. Each card's position
will be updated to match its index in the list.

## Examples

    iex> reorder_dashboard_cards(dashboard, [card3_id, card1_id, card2_id])
    :ok

# `run_dashboard`

```elixir
@spec run_dashboard(
  Lotus.Storage.Dashboard.t() | id(),
  keyword()
) :: %{required(id()) =&gt; {:ok, Lotus.Result.t()} | {:error, term()}}
```

Runs all query cards in a dashboard and returns their results.

Returns a map of card IDs to their results. By default, cards are executed
in parallel for better performance.

## Options

  * `:filter_values` - Map of filter names to their current values
  * `:parallel` - Whether to run cards in parallel (default: true)
  * `:timeout` - Timeout per card in milliseconds (default: 30000)

## Filter Resolution

Filter values are resolved to query variables through the configured mappings.
For each card:
1. Get all filter mappings for the card
2. For each mapping, get the filter value from `:filter_values`
3. Apply any configured transform to the value
4. Pass the value to the query as the mapped variable name

## Examples

    iex> run_dashboard(dashboard, filter_values: %{"date_range" => "2024-01-01"})
    %{
      1 => {:ok, %Lotus.Result{}},
      2 => {:ok, %Lotus.Result{}},
      3 => {:error, "Missing required variable: status"}
    }

# `run_dashboard_card`

```elixir
@spec run_dashboard_card(
  Lotus.Storage.DashboardCard.t() | id(),
  keyword()
) :: {:ok, Lotus.Result.t()} | {:error, term()}
```

Runs a single dashboard card and returns its result.

## Options

  * `:filter_values` - Map of filter names to their current values
  * `:timeout` - Query timeout in milliseconds

## Examples

    iex> run_dashboard_card(card, filter_values: %{"user_id" => "123"})
    {:ok, %Lotus.Result{}}

# `update_dashboard`

```elixir
@spec update_dashboard(Lotus.Storage.Dashboard.t(), attrs()) ::
  {:ok, Lotus.Storage.Dashboard.t()} | {:error, Ecto.Changeset.t()}
```

Updates a dashboard.

## Examples

    iex> update_dashboard(dashboard, %{name: "New Name"})
    {:ok, %Dashboard{}}

# `update_dashboard_card`

```elixir
@spec update_dashboard_card(Lotus.Storage.DashboardCard.t(), attrs()) ::
  {:ok, Lotus.Storage.DashboardCard.t()} | {:error, Ecto.Changeset.t()}
```

Updates a card.

## Examples

    iex> update_dashboard_card(card, %{title: "Revenue Chart"})
    {:ok, %DashboardCard{}}

# `update_dashboard_filter`

```elixir
@spec update_dashboard_filter(Lotus.Storage.DashboardFilter.t(), attrs()) ::
  {:ok, Lotus.Storage.DashboardFilter.t()} | {:error, Ecto.Changeset.t()}
```

Updates a filter.

## Examples

    iex> update_dashboard_filter(filter, %{label: "Select Period"})
    {:ok, %DashboardFilter{}}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
