Skip to content

Commit

Permalink
Refactor UI and update deps
Browse files Browse the repository at this point in the history
  • Loading branch information
simonprev committed Sep 13, 2024
1 parent cb94a8a commit 0b5ea92
Show file tree
Hide file tree
Showing 15 changed files with 130 additions and 103 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,21 @@ scope "/" do
end
```

### Image generation

Optionally, you can add 2 dependencies to generate png images from the charts. When you use the share feature, every chart will have an associated image (hidden) that will render a nice preview when sharing on Slack/other platforms.

```elixir
# mix.exs
def deps do
[
#...
{:vix, "~> 0.26"},
{:vega_lite_convert, "~> 0.6"},
]
end
```

#### Security

Since it may contain sensitive data, `TelemetryUI` requires a special assign to render the page.
Expand All @@ -158,7 +173,7 @@ That’s it! You can declare as many metrics as you want and they will render in

## License

`TelemetryUI` is © 2023 [Mirego](https://www.mirego.com) and may be freely distributed under the [New BSD license](http://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/telemetry_ui/blob/master/LICENSE.md) file.
`TelemetryUI` is © 2024 [Mirego](https://www.mirego.com) and may be freely distributed under the [New BSD license](http://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/telemetry_ui/blob/master/LICENSE.md) file.

## About Mirego

Expand Down
16 changes: 0 additions & 16 deletions assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,6 @@ body {
z-index: 1005;
}

html.dark .dropdown .dropdown-menu::after {
border-bottom-color: #000;
}

.dropdown .dropdown-menu::after {
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid #fff;
content: '';
height: 0;
position: absolute;
top: -7px;
width: 0;
right: 10px;
}

.dropdown .dropdown-menu::before {
content: '';
position: absolute;
Expand Down
46 changes: 26 additions & 20 deletions assets/js/app.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as vega from 'vega';
import vegaEmbed from 'vega-embed';
import vegaEmbed, {VisualizationSpec} from 'vega-embed';

declare global {
var drawChart: (id: string, spec: string) => void;
var drawChart: (id: string, spec: VisualizationSpec) => void;
}

interface ViewInternal {
Expand Down Expand Up @@ -69,7 +69,7 @@ const onSelectTag = (
}
};

const resetLegendSelect = (view: View, legendItems: HTMLElement[]) => {
const resetLegendSelect = (view: vega.View, legendItems: HTMLElement[]) => {
legendItems.forEach((item) => {
item.removeAttribute('data-selected');
item.classList.remove('opacity-50');
Expand All @@ -78,11 +78,14 @@ const resetLegendSelect = (view: View, legendItems: HTMLElement[]) => {
view.signal('tags_tags_legend', null).run();
};

const bindLegend = (element: HTMLElement, id: string, viewUnknown: unknown) => {
const legend = document.querySelector(id + '-legend');
const bindLegend = (
legend: HTMLElement,
element: HTMLElement,
viewUnknown: unknown
) => {
const runtime = (viewUnknown as ViewInternal)._runtime;
const signals = (viewUnknown as ViewInternal)._signals;
const view = viewUnknown as View;
const view = viewUnknown as vega.View;

if (!legend || !runtime.scales.color || !signals.tags_tags_legend) return;

Expand Down Expand Up @@ -123,12 +126,11 @@ const bindLegend = (element: HTMLElement, id: string, viewUnknown: unknown) => {
legend.addEventListener('click', () => resetLegendSelect(view, legendItems));
};

const renderLegend = (id: string, viewUnknown: unknown) => {
const renderLegend = (legend: HTMLElement, viewUnknown: unknown) => {
const runtime = (viewUnknown as ViewInternal)._runtime;
const view = viewUnknown as View;
const view = viewUnknown as vega.View;

const legend = document.querySelector(id + '-legend');
if (!legend || !runtime.scales.color) return;
if (!runtime.scales.color) return;

legend.classList.remove('hidden');
const itemsCount = Array.from(
Expand Down Expand Up @@ -214,14 +216,14 @@ const toggleFullscreen = (parentElement: HTMLElement, id: string) => {
parentElement.classList.remove(...fixedStyle);
parentElement.classList.add('relative');
parentElement.classList.add('dark:bg-black/40');
closeButton.classList.add('hidden');
closeButton?.classList.add('hidden');

viewsById[id].signal('height', heightsById[id]);
} else {
parentElement.classList.add(...fixedStyle);
parentElement.classList.remove('relative');
parentElement.classList.remove('dark:bg-black/40');
closeButton.classList.remove('hidden');
closeButton?.classList.remove('hidden');

heightsById[id] = viewsById[id].signal('height');
viewsById[id].signal('height', window.innerHeight - 130);
Expand All @@ -230,7 +232,7 @@ const toggleFullscreen = (parentElement: HTMLElement, id: string) => {
window.dispatchEvent(new Event('resize'));
};

window.drawChart = async (id, spec) => {
window.drawChart = async (id, spec: VisualizationSpec) => {
const {view} = await vegaEmbed(id, spec, {
renderer: 'svg',
actions: {
Expand All @@ -241,9 +243,11 @@ window.drawChart = async (id, spec) => {
}
});

if (spec.data.url) {
const specData = spec.data as vega.UrlData;

if (specData?.url) {
setInterval(async () => {
const response = await fetch(spec.data.url);
const response = await fetch(String(specData.url));
const data = await response.json();

const now = Number(new Date());
Expand Down Expand Up @@ -273,26 +277,28 @@ window.drawChart = async (id, spec) => {
viewsById[id] = view;
};

const setSideElements = (view, id) => {
const setSideElements = (view: vega.View, id: string) => {
const title = document.querySelector(id + '-title') as HTMLElement;
const element = document.querySelector(id) as HTMLElement;
const legend = document.querySelector(id + '-legend') as HTMLElement;
const legend = document.querySelector(id + '-legend') as HTMLElement | null;
const empty = document.querySelector(id + '-empty') as HTMLElement;

if (emptySource(view.data('source'))) {
title.classList.remove('hidden');
empty.classList.remove('hidden');
element.classList.add('hidden');
element.classList.remove('vega-embed');
legend.classList.add('hidden');
legend?.classList.add('hidden');
} else {
title.classList.add('hidden');
empty.classList.add('hidden');
element.classList.remove('hidden');
element.classList.add('vega-embed');

const renderedLegend = renderLegend(id, view);
if (renderedLegend) bindLegend(element, id, view);
if (legend) {
const renderedLegend = renderLegend(legend, view);
if (renderedLegend) bindLegend(legend, element, view);
}
}
};

Expand Down
37 changes: 17 additions & 20 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -1,35 +1,32 @@
import Config

config :phoenix, :json_library, Jason
config :phoenix, :stacktrace_depth, 20
alias TelemetryUI.Test.Repo

config :logger, level: :warn
config :logger, :console, format: "[$level] $message\n"
config :logger, level: :warn

if config_env() in [:dev, :test] do
config :tailwind,
version: "3.1.6",
default: [
args: ~w(
--config=tailwind.config.js
--input=css/app.css
--output=../dist/app.css
--minify
),
cd: Path.expand("../assets", __DIR__)
]
config :phoenix, :json_library, Jason
config :phoenix, :stacktrace_depth, 20

if config_env() in [:dev, :test] do
config :esbuild,
version: "0.14.41",
default: [
args: ~w(js/app.ts --bundle --target=es2020 --outdir=../dist --minify --tree-shaking=true --bundle),
version: "0.17.11",
telemetry_ui: [
args: ~w(js/app.ts --outdir=../priv/static/assets --bundle),
cd: Path.expand("../assets", __DIR__),
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
]

config :tailwind,
version: "3.2.7",
telemetry_ui: [
args: ~w(--config=tailwind.config.js --input=css/app.css --output=../priv/static/assets/app.css),
cd: Path.expand("../assets", __DIR__)
]
end

if config_env() in [:test] do
config :telemetry_ui, TelemetryUI.Test.Repo,
config :telemetry_ui, Repo,
priv: "test/support/",
pool: Ecto.Adapters.SQL.Sandbox,
url: System.get_env("DATABASE_URL", "postgres://postgres:development@localhost/telemetry_ui_test")
Expand All @@ -39,4 +36,4 @@ if config_env() in [:test] do
render_errors: [view: TelemetryUI.Test.ErrorView]
end

config :telemetry_ui, ecto_repos: [TelemetryUI.Test.Repo]
config :telemetry_ui, ecto_repos: [Repo]
2 changes: 1 addition & 1 deletion dist/app.css

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions lib/telemetry_ui/slug.ex
Original file line number Diff line number Diff line change
Expand Up @@ -671,11 +671,11 @@ defmodule TelemetryUI.Slug do
replacements_by_search =
replacements
|> Enum.group_by(fn {search, _} -> search end, fn {_, replace} -> replace end)
|> Enum.into([])
|> Enum.to_list()

# Create replacement
for {search, replaces} <- replacements_by_search do
if search != @separator_char do
unless search == @separator_char do
replace = hd(replaces)
defp replace_chars([unquote(search) | t]), do: unquote(replace) ++ replace_chars(t)
end
Expand Down
12 changes: 10 additions & 2 deletions lib/telemetry_ui/web/components/stat.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,21 @@ defmodule TelemetryUI.Web.Components.Stat do
compare_layer,
Vl.new()
|> Vl.transform(filter: "datum.compare==0")
|> Vl.mark(:area, opacity: 0.2, tooltip: true, color: hd(assigns.theme.scale), y_offset: chart_offset, y2_offset: chart_offset, y2: [expr: "height + #{chart_offset}"])
|> Vl.mark(:area,
opacity: 0.2,
interpolate: "monotone",
tooltip: true,
color: hd(assigns.theme.scale),
y_offset: chart_offset,
y2_offset: chart_offset,
y2: [expr: "height + #{chart_offset}"]
)
|> Vl.encode(:tooltip, tooltip)
|> Vl.encode_field(:x, "date", type: :temporal, title: nil, axis: nil, time_unit: [unit: time_unit])
|> Vl.encode_field(:y, options.field, type: :quantitative, title: nil, axis: nil, aggregate: options.summary_aggregate || options.aggregate),
Vl.new()
|> Vl.transform(filter: "datum.compare==0")
|> Vl.mark(:line, opacity: 0.3, color: hd(assigns.theme.scale), y_offset: chart_offset, y2_offset: chart_offset)
|> Vl.mark(:line, opacity: 0.3, interpolate: "monotone", color: hd(assigns.theme.scale), y_offset: chart_offset, y2_offset: chart_offset)
|> Vl.encode_field(:x, "date", type: :temporal, title: nil, axis: nil, time_unit: [unit: time_unit])
|> Vl.encode_field(:y, options.field, type: :quantitative, title: nil, axis: nil, aggregate: options.summary_aggregate || options.aggregate)
])
Expand Down
4 changes: 3 additions & 1 deletion lib/telemetry_ui/web/components/time_series.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ defmodule TelemetryUI.Web.Components.TimeSeries do
clip: true,
align: "left",
baseline: "line-bottom",
interpolate: "monotone",
tooltip: true,
width: [band: 0.6],
fill_opacity: 1,
Expand Down Expand Up @@ -68,9 +69,10 @@ defmodule TelemetryUI.Web.Components.TimeSeries do
|> Vl.mark(:area,
clip: true,
align: "left",
interpolate: "monotone",
baseline: "line-bottom",
point: [size: 14],
line: [stroke_width: 1],
line: [stroke_width: 1, stroke_opacity: 0.8],
tooltip: true,
fill_opacity: 0.3,
color: hd(assigns.theme.scale)
Expand Down
21 changes: 11 additions & 10 deletions lib/telemetry_ui/web/template.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<link rel="icon" href={favicon(@theme)} type="image/svg+xml" />
</head>

<body class="flex flex-col justify-between min-h-screen bg-neutral-50 dark:bg-neutral-900">
<body class="flex flex-col justify-between min-h-screen bg-white dark:bg-black">
<script type="text/javascript">
(function () {
try {
Expand Down Expand Up @@ -54,8 +54,8 @@
<.theme_switch />
</div>
<% else %>
<div class="bg-white dark:bg-black shadow-sm">
<header class="max-w-6xl mx-auto flex justify-between p-2 mb-4" style={theme_color_style(@theme)}>
<div class="bg-white dark:bg-black">
<header class="max-w-6xl mx-auto flex justify-between p-2 mb-4 mt-12" style={theme_color_style(@theme)}>
<a class="flex items-center gap-3 text-base font-light font-mono" href={page_href(List.first(@pages).id, %{frame: nil})}>
<%= {:safe, @theme.logo} %><%= @theme.title %>
</a>
Expand All @@ -74,7 +74,10 @@
</select>

<div class="dropdown inline-block relative">
<button onclick="event.preventDefault()" class="p-2 rounded-md bg-transparent border-black/10 dark:border-slate-50/10 text-gray-500 dark:text-gray-200 text-xs">
<button
onclick="event.preventDefault()"
class="cursor-pointer p-2 rounded-md bg-transparent border-black/10 dark:border-slate-50/10 text-gray-500 dark:text-gray-200 text-xs"
>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-5">
<path
stroke-linecap="round"
Expand All @@ -85,7 +88,7 @@
</svg>
</button>

<ul class="dropdown-menu w-[160px] absolute hidden flex-col gap-3 bg-white dark:bg-black shadow rounded mt-3 text-gray-700 right-[-3px] p-3">
<ul class="dropdown-menu w-[160px] absolute hidden flex-col gap-3 bg-white dark:bg-black rounded shadow-lg mt-2 dark:border dark:border-white/10 text-gray-700 right-[-3px] p-3">
<%= if @share do %>
<a href={"#{@theme.share_path}?share=#{@share}"} target="_blank" class="flex items-center gap-1 dark:text-neutral-200 text-neutral-500 text-xs">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
Expand All @@ -99,9 +102,7 @@
</a>
<% end %>

<.theme_switch>
Switch theme
</.theme_switch>
<.theme_switch>Switch theme</.theme_switch>
</ul>
</div>
</.form>
Expand All @@ -111,14 +112,14 @@
<% end %>

<%= if not @shared and length(@pages) > 1 do %>
<div class="max-w-6xl mx-auto flex flex-wrap gap-3 mb-4">
<div class="max-w-6xl mx-auto flex flex-wrap gap-1 mb-4 -left-4 relative">
<%= for page <- @pages do %>
<%= if page.id === @current_page.id do %>
<.page_link
page={page}
filters={@filters}
style={theme_color_style(@theme)}
class="px-4 py-1 shadow-sm bg-white dark:bg-black font-bold text-primary text-sm dark:text-gray-50"
class="px-4 py-1 bg-white dark:bg-black font-bold text-primary text-sm dark:text-gray-50"
/>
<% else %>
<.page_link page={page} filters={@filters} class="transition px-4 py-1 font-bold text-sm dark:text-gray-50 hover:opacity-50" />
Expand Down
4 changes: 4 additions & 0 deletions lib/telemetry_ui/web/vega_lite/spec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,15 @@ defmodule TelemetryUI.Web.VegaLite.Spec do
grid_color = "rgba(0,0,0,0.1)"
label_color = "#666"

font =
~s|ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"|

[
autosize: [type: "fit-x"],
axis_y: [domain_color: label_color, label_color: label_color, tick_color: label_color, grid_color: grid_color],
axis_x: [domain_color: label_color, label_color: label_color, tick_color: label_color, grid_color: grid_color],
view: [stroke: nil],
font: font,
range: [category: assigns.theme.scale]
]
end
Expand Down
Loading

0 comments on commit 0b5ea92

Please sign in to comment.