245 lines
7.1 KiB
Markdown
245 lines
7.1 KiB
Markdown
[](https://github.com/elinverd/luminous/actions/workflows/test.yml)
|
|
[](https://hex.pm/packages/luminous)
|
|
|
|
# Luminous
|
|
|
|
Luminous is a framework for creating dashboards within [Phoenix Live
|
|
View](https://www.phoenixframework.org/).
|
|
|
|
Dashboards are defined by the client application (framework consumer)
|
|
using elixir code and consist of Panels (`Luminous.Panel`) which are
|
|
responsible for visualizing the results of multiple client-side
|
|
queries (`Luminous.Query`).
|
|
|
|
Three different types of Panels are currently offered out of the box
|
|
by Luminous:
|
|
|
|
- `Luminous.Panel.Chart` for visualizing 2-d data (including time
|
|
series) using the [chartjs](https://www.chartjs.org/) library
|
|
(embedded in a JS hook). Currently, only `:line` and `:bar`are
|
|
supported.
|
|
- `Luminous.Panel.Stat` for displaying single or multiple numerical or
|
|
other values (e.g. strings)
|
|
- `Luminous.Panel.Table` for displaying tabular data
|
|
|
|
A client application can implement its own custom panels by
|
|
implementing the `Luminous.Panel` behaviour.
|
|
|
|
Dashboards are parameterized by:
|
|
|
|
- a date range (using the [flatpickr](https://flatpickr.js.org/) library)
|
|
- user-defined variables (`Luminous.Variable`) in the form of dropdown menus
|
|
|
|
All panels are refreshed whenever at least one of these paramaters
|
|
(date range, variables) change. The parameter values are available to
|
|
client-side queries.
|
|
|
|
## Features
|
|
|
|
- Date range selection and automatic asynchronous (i.e. non-blocking
|
|
for the UI) refresh of all dashboard panel queries
|
|
- User-facing variable dropdowns (with single- or multi- selection)
|
|
whose selected values are available to panel queries
|
|
- Client-side zoom in charts with automatic update of the entire
|
|
dashboard with the new date range
|
|
- Panel data downloads depending on the panel type (CSV, PNG)
|
|
- Stat panels (show single or multiple stats)
|
|
- Table panels using [tabulator](https://tabulator.info/)
|
|
- Summary statistics in charts
|
|
|
|
## Installation
|
|
|
|
The package can be installed from `hex.pm` as follows:
|
|
|
|
```elixir
|
|
def deps do
|
|
[
|
|
{:luminous, "~> 2.6.1"}
|
|
]
|
|
end
|
|
```
|
|
|
|
In order to be able to use the provided components, the library's
|
|
`javascript` and `CSS` files must be imported to your project:
|
|
|
|
In `assets/js/app.js`:
|
|
|
|
```javascript
|
|
import { ChartJSHook, TableHook, TimeRangeHook, MultiSelectVariableHook } from "luminous"
|
|
|
|
let Hooks = {
|
|
TimeRangeHook: new TimeRangeHook(),
|
|
ChartJSHook: new ChartJSHook(),
|
|
TableHook: new TableHook(),
|
|
MultiSelectVariableHook: new MultiSelectVariableHook()
|
|
}
|
|
|
|
...
|
|
|
|
let liveSocket = new LiveSocket("/live", Socket, {
|
|
...
|
|
hooks: Hooks
|
|
})
|
|
...
|
|
```
|
|
|
|
Finally, in `assets/css/app.css`:
|
|
```CSS
|
|
@import "../../deps/luminous/dist/luminous.css";
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Live View
|
|
|
|
The dashboard live view is defined client-side like so:
|
|
|
|
```elixir
|
|
defmodule ClientApp.DashboardLive do
|
|
alias ClientApp.Router.Helpers, as: Routes
|
|
|
|
use Luminous.Live,
|
|
title: "My Title",
|
|
time_zone: "Europe/Paris",
|
|
panels: [
|
|
...
|
|
],
|
|
variables: [
|
|
...
|
|
]
|
|
|
|
# the dashboard can be rendered by leveraging the corresponding functionality
|
|
# from `Luminous.Components`
|
|
def render(assigns) do
|
|
~H"""
|
|
<Luminous.Components.dashboard dashboard={@dashboard} />
|
|
"""
|
|
end
|
|
|
|
# we also need to implement the function that generates the LV path
|
|
@impl Luminous.Dashboard
|
|
def dashboard_path(socket, url_params) do
|
|
Routes.dashboard_path(socket, :index, url_params)
|
|
end
|
|
end
|
|
```
|
|
|
|
The client-side dashboard can also (optionally) implement the
|
|
`Luminous.TimeRange` behaviour in order to override the dashboard's
|
|
default time range value which is "today".
|
|
|
|
### Panels and Queries
|
|
|
|
Client-side queries must be included in a module that implements the
|
|
`Luminous.Query` behaviour:
|
|
|
|
```elixir
|
|
defmodule ClientApp.DashboardLive do
|
|
|
|
defmodule Queries do
|
|
@behaviour Luminous.Query
|
|
|
|
@impl true
|
|
def query(:my_query, _time_range, _variables) do
|
|
[
|
|
[{:time, ~U[2022-08-19T10:00:00Z]}, {"foo", 10}, {"bar", 100}],
|
|
[{:time, ~U[2022-08-19T11:00:00Z]}, {"foo", 11}, {"bar", 101}]
|
|
]
|
|
end
|
|
end
|
|
|
|
use Luminous.Live,
|
|
...
|
|
panels: [
|
|
Panel.define!(
|
|
type: Luminous.Panel.Chart,
|
|
id: :simple_time_series,
|
|
title: "Simple Time Series",
|
|
queries: [
|
|
Luminous.Query.define(:my_query, Queries)
|
|
],
|
|
description: """
|
|
This will be rendered as a tooltip
|
|
when hovering over the panel's title
|
|
"""
|
|
),
|
|
],
|
|
...
|
|
end
|
|
```
|
|
|
|
A panel may include multiple queries. When a panel is automatically
|
|
refreshed, the execution flow is as follows:
|
|
|
|
- for each query:
|
|
- execute the user query callback
|
|
- execute the panel's `transform/2` callback with the query result output
|
|
- aggregate the transformed query results
|
|
- update the dashboard state variable with the panel's data
|
|
(possible server-side re-rendering)
|
|
- send a JS event to the browser (for panel hooks)
|
|
|
|
The above flow needs to be understood when implementing custom
|
|
panels. If the client application uses the panels provided by
|
|
luminous, then the panel refresh flow is handled automatically and
|
|
only `use Luminous.Live` with the appropriate options is necessary.
|
|
|
|
### Variables
|
|
|
|
Variables represent user-facing elements in the form of dropdowns in
|
|
which the user can select single (variable type: `:single`) or
|
|
multiple (variable type: `:multi`) values.
|
|
|
|
Variable selections trigger the refresh of all panels in the
|
|
dashboard. The state of all variables is available within the `query`
|
|
callback that is implemented by the client application.
|
|
|
|
Just like queries, variables must be included in a module that
|
|
implements the `Luminous.Variable` behaviour:
|
|
|
|
```elixir
|
|
defmodule ClientApp.DashboardLive do
|
|
|
|
defmodule Variables do
|
|
@behaviour Luminous.Variable
|
|
|
|
@impl true
|
|
def variable(:simple_var, _assigns), do: ["hour", "day", "week"]
|
|
|
|
def variable(:descriptive_var, _assigns) do
|
|
[
|
|
%{label: "Visible Value 1", value: "val1"},
|
|
%{label: "Visible Value 2", value: "val2"},
|
|
]
|
|
end
|
|
end
|
|
|
|
use Luminous.Live,
|
|
...
|
|
variables: [
|
|
Luminous.Variable.define!(id: :simple_var, label: "Select one value", module: Variables),
|
|
Luminous.Variable.define!(id: :descriptive_var, label: "Select one value", module: Variables),
|
|
],
|
|
...
|
|
end
|
|
```
|
|
|
|
The variable callback will receive the live view socket assigns as the
|
|
second argument, however it is important to note that the `variable/2`
|
|
callback is executed once when the dashboard is loaded for populating
|
|
the dropdown values.
|
|
|
|
A `Variable` can be marked as hidden by passing `hidden: true` to
|
|
`Variable.define!/1`. Hidden variables are a means for framework
|
|
clients to store some kind of state expecially in the case of custom
|
|
Panels (a typical use case is pagination). As such, hidden variables
|
|
are not rendered as dropdowns in the dashboard and are not included in
|
|
URL params.
|
|
|
|
### Demo
|
|
|
|
Luminous provides a demo dashboard that showcases some of Luminous'
|
|
capabilities. The demo dashboard can be inspected live using the
|
|
project's development server (run `mix run` in the project and then
|
|
visit [this page](http://localhost:5000)).
|