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 """
pidnameinitial call memory (KB)msgsstatus
""" 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||, ~s|#{h(node["pid"])}|, ~s|#{h(name || "-")}|, ~s|#{h(node["initial_call"])}|, ~s|#{node["memory_kb"]}|, ~s|#{node["msgs"]}|, ~s|#{h(node["status"])}|, "" ] end # ── modules panel ───────────────────────────────────────────────── def modules_panel do """
moduleappsource
""" 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|| <> ~s|#{h(r["module"])}| <> ~s|#{h(r["app"] || "-")}| <> ~s|#{h(r["source"] || "(no source)")}| <> "" 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