init
This commit is contained in:
126
lib/forum/admin.ex
Normal file
126
lib/forum/admin.ex
Normal file
@@ -0,0 +1,126 @@
|
||||
defmodule Forum.Admin do
|
||||
@moduledoc """
|
||||
HTML fragments served to the HTMX admin page (/admin_htmx). Two
|
||||
panels (processes, modules) plus their row-only partials.
|
||||
"""
|
||||
|
||||
# ── processes panel ───────────────────────────────────────────────
|
||||
|
||||
def processes_panel do
|
||||
"""
|
||||
<section>
|
||||
<form class="toolbar"
|
||||
hx-get="/admin_htmx/processes/rows"
|
||||
hx-target="#p-rows"
|
||||
hx-trigger="change">
|
||||
<label><input type="checkbox" name="mine"> mine</label>
|
||||
<span class="htmx-indicator">…</span>
|
||||
</form>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>pid</th><th>name</th><th>initial call</th>
|
||||
<th class="num">memory (KB)</th><th class="num">msgs</th><th>status</th>
|
||||
</tr></thead>
|
||||
<tbody id="p-rows"
|
||||
hx-get="/admin_htmx/processes/rows"
|
||||
hx-trigger="load, every 5s"
|
||||
hx-include="closest section form"></tbody>
|
||||
</table>
|
||||
</section>
|
||||
"""
|
||||
end
|
||||
|
||||
def processes_rows(rows) do
|
||||
rows
|
||||
|> build_tree()
|
||||
|> render_tree(0)
|
||||
|> IO.iodata_to_binary()
|
||||
end
|
||||
|
||||
defp build_tree(rows) do
|
||||
by_parent = Enum.group_by(rows, & &1["parent"])
|
||||
Map.get(by_parent, nil, []) |> Enum.map(&attach_children(&1, by_parent))
|
||||
end
|
||||
|
||||
defp attach_children(node, by_parent) do
|
||||
children = Map.get(by_parent, node["pid"], [])
|
||||
Map.put(node, "children", Enum.map(children, &attach_children(&1, by_parent)))
|
||||
end
|
||||
|
||||
defp render_tree(nodes, depth) do
|
||||
Enum.map(nodes, fn node ->
|
||||
[process_row(node, depth) | render_tree(node["children"], depth + 1)]
|
||||
end)
|
||||
end
|
||||
|
||||
defp process_row(node, depth) do
|
||||
row_cls = if node["mine"], do: ~s| class="mine"|, else: ""
|
||||
pad = 8 + depth * 16
|
||||
name = node["name"]
|
||||
name_cls = if name, do: ~s| class="name"|, else: ""
|
||||
|
||||
[
|
||||
~s|<tr#{row_cls}>|,
|
||||
~s|<td style="padding-left:#{pad}px">#{h(node["pid"])}</td>|,
|
||||
~s|<td#{name_cls}>#{h(name || "-")}</td>|,
|
||||
~s|<td>#{h(node["initial_call"])}</td>|,
|
||||
~s|<td class="num">#{node["memory_kb"]}</td>|,
|
||||
~s|<td class="num">#{node["msgs"]}</td>|,
|
||||
~s|<td>#{h(node["status"])}</td>|,
|
||||
"</tr>"
|
||||
]
|
||||
end
|
||||
|
||||
# ── modules panel ─────────────────────────────────────────────────
|
||||
|
||||
def modules_panel do
|
||||
"""
|
||||
<section>
|
||||
<form class="toolbar"
|
||||
hx-get="/admin_htmx/modules/rows"
|
||||
hx-target="#m-rows"
|
||||
hx-trigger="change, input changed delay:200ms from:input[name=q]">
|
||||
<label><input type="checkbox" name="mine"> mine</label>
|
||||
<input type="text" name="q" placeholder="filter…" autocomplete="off">
|
||||
<span class="htmx-indicator">…</span>
|
||||
</form>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>module</th><th>app</th><th>source</th>
|
||||
</tr></thead>
|
||||
<tbody id="m-rows"
|
||||
hx-get="/admin_htmx/modules/rows"
|
||||
hx-trigger="load"
|
||||
hx-include="closest section form"></tbody>
|
||||
</table>
|
||||
</section>
|
||||
"""
|
||||
end
|
||||
|
||||
def modules_rows(rows) do
|
||||
Enum.map_join(rows, "", &module_row/1)
|
||||
end
|
||||
|
||||
defp module_row(r) do
|
||||
row_cls = if r["mine"], do: ~s| class="mine"|, else: ""
|
||||
|
||||
~s|<tr#{row_cls}>| <>
|
||||
~s|<td class="mod">#{h(r["module"])}</td>| <>
|
||||
~s|<td>#{h(r["app"] || "-")}</td>| <>
|
||||
~s|<td class="src">#{h(r["source"] || "(no source)")}</td>| <>
|
||||
"</tr>"
|
||||
end
|
||||
|
||||
# ── helpers ───────────────────────────────────────────────────────
|
||||
|
||||
defp h(text) when is_binary(text) do
|
||||
text
|
||||
|> String.replace("&", "&")
|
||||
|> String.replace("<", "<")
|
||||
|> String.replace(">", ">")
|
||||
|> String.replace("\"", """)
|
||||
|> String.replace("'", "'")
|
||||
end
|
||||
|
||||
defp h(other), do: other |> to_string() |> h()
|
||||
end
|
||||
Reference in New Issue
Block a user