Current section
Files
Jump to
Current section
Files
lib/web/components/layouts.ex
defmodule Observer.Web.Layouts do
@moduledoc """
This module provides layouts
## References:
* https://github.com/oban-bg/oban_web/blob/main/lib/oban/web/components/layouts.ex
"""
use Observer.Web, :html
embed_templates "layouts/*"
alias Observer.Web.Assets
alias Observer.Web.Components.Icons
defp asset_path(conn, asset) when asset in [:css, :js] do
hash = Assets.current_hash(asset)
{_dash, _routing, meta} = conn.private.phoenix_live_view
prefix = get_in(meta, [:extra, :session, Access.elem(2), Access.at(0)])
Phoenix.VerifiedRoutes.unverified_path(
conn,
conn.private.phoenix_router,
"#{prefix}/#{asset}-#{hash}"
)
end
attr :params, :map, required: true
attr :path, :string, default: nil
def logo(assigns) do
~H"""
<a href={@path || observer_path(:system, @params)} class="flex" title="Observer Web">
<div>
<Icons.content name={:logo} />
</div>
<h3 class="class ml-2 items-center tracking-tight ">
<span class="block text-4xl text-gray-900 dark:text-white">
Observer
<span class="text-transparent text-4xl font-bold bg-clip-text bg-gradient-to-tr to-cyan-500 from-blue-600">
WEB
</span>
</span>
</h3>
</a>
"""
end
attr :params, :map, required: true
attr :page, :atom, required: true
attr :socket, :map, required: true
attr :custom_pages, :list, default: []
def nav(assigns) do
~H"""
<nav id="main-nav" phx-hook="NavOverflow" class="relative flex-1 min-w-0">
<div data-nav-items class="flex items-center gap-1 overflow-hidden">
<.link
:for={page <- list_pages_by_params(@params) ++ Keyword.keys(@custom_pages)}
class={link_class(@page, page)}
data-shortcut={JS.navigate(observer_path(page, @params))}
id={"nav-#{page}"}
navigate={observer_path(page, @params)}
title={"Navigate to #{String.capitalize(to_string(page))}"}
>
<Icons.content name={page} />
{String.upcase(to_string(page))}
</.link>
<button
id="nav-more"
data-nav-more
hidden
class="flex items-center shrink-0 px-4 py-2.5 text-sm font-bold transition-all duration-200 text-gray-900 dark:text-white rounded-lg hover:text-black dark:hover:text-white hover:bg-cyan-500 dark:hover:bg-cyan-800"
>
MORE
<svg
class="w-4 h-4 ml-1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
</div>
<div
id="nav-more-panel"
data-nav-panel
hidden
class="absolute right-0 top-full z-50 mt-1 flex flex-col gap-1 rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 shadow-lg p-1"
>
</div>
</nav>
"""
end
def footer(assigns) do
assigns =
assign(assigns,
oss_version: Application.spec(:observer_web, :vsn)
)
~H"""
<footer class="flex flex-col px-3 py-6 text-sm justify-center items-center md:flex-row">
<.version name="Observer Web" version={@oss_version} />
<span class="text-gray-800 dark:text-gray-200 font-semibold">
Made by
<a
href="https://www.linkedin.com/in/thiago-cesar-calori-esteves-972368115/"
class="font-medium text-blue-600 underline dark:text-blue-500 hover:no-underline"
>
Thiago Esteves
</a>
</span>
</footer>
"""
end
attr :name, :string
attr :version, :string
defp version(assigns) do
~H"""
<span class="text-gray-600 dark:text-gray-400 tabular mr-0 mb-1 md:mr-3 md:mb-0">
{@name} {if @version, do: "v#{@version}", else: "–"}
</span>
"""
end
defp link_class(curr, page) do
base =
"flex items-center px-4 py-2.5 text-sm font-bold transition-all duration-200 text-gray-900 dark:text-white rounded-lg group"
if curr == page do
base <> " bg-gray-100 dark:bg-gray-700"
else
base <> " hover:text-black dark:hover:text-white hover:bg-cyan-500 dark:hover:bg-cyan-800"
end
end
# Crashdump sits last since it is an occasional-use tool (see ObserverWeb.Crashdump).
defp list_pages_by_params(%{"iframe" => "true"}) do
[
:system,
:tracing,
:profiling,
:processes,
:applications,
:network,
:ets,
:metrics,
:logs,
:crashdump
]
end
defp list_pages_by_params(_params) do
[
:root,
:system,
:tracing,
:profiling,
:processes,
:applications,
:network,
:ets,
:metrics,
:logs,
:crashdump
]
end
@doc """
Renders flash notices.
## Examples
<.flash kind={:info} flash={@flash} />
<.flash kind={:info} phx-mounted={show("#flash")}>Welcome Back!</.flash>
"""
attr :id, :string, doc: "the optional id of flash container"
attr :flash, :map, default: %{}, doc: "the map of flash messages to display"
attr :title, :string, default: nil
attr :kind, :atom, values: [:info, :error], doc: "used for styling and flash lookup"
attr :rest, :global, doc: "the arbitrary HTML attributes to add to the flash container"
slot :inner_block, doc: "the optional inner block that renders the flash message"
def flash(assigns) do
assigns = assign_new(assigns, :id, fn -> "flash-#{assigns.kind}" end)
~H"""
<div
:if={msg = render_slot(@inner_block) || Phoenix.Flash.get(@flash, @kind)}
id={@id}
phx-hook="AutoDismissFlash"
phx-click={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
role="alert"
class="fixed z-40 inset-0 flex items-end justify-center pointer-events-none md:py-3 md:px-4 sm:p-6 sm:items-start sm:justify-end"
{@rest}
>
<div class="max-w-sm w-full bg-white dark:bg-black dark:bg-opacity-90 shadow-lg rounded-lg pointer-events-auto">
<div class="rounded-lg ring-1 ring-black/5 overflow-hidden">
<div class="p-4">
<div class="flex items-start">
<%= if @kind == :error do %>
<div class="flex-shrink-0 text-red-400">
<Icons.x_circle />
</div>
<% else %>
<div class="flex-shrink-0 text-green-400">
<Icons.check_circle />
</div>
<% end %>
<div class="ml-3 w-0 flex-1 pt-0.5">
<p class="text-sm leading-5 font-medium text-gray-900 dark:text-gray-100">
{msg}
</p>
</div>
<div class="ml-4 flex-shrink-0 flex">
<button
phx-click="lv:clear-flash"
class="inline-flex text-gray-400 dark:text-gray-600 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 focus:text-gray-500 transition ease-in-out duration-150"
>
<Icons.x_mark class="w-5 h-5" />
</button>
</div>
</div>
</div>
</div>
</div>
</div>
"""
end
def hide(js \\ %JS{}, selector) do
JS.hide(js,
to: selector,
time: 200,
transition:
{"transition-all transform ease-in duration-200",
"opacity-100 translate-y-0 sm:scale-100",
"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"}
)
end
end