defmodule DalaDev.Server.ClusterVizLive do @moduledoc """ LiveView for cluster visualization with D3.js. Provides real-time visualization of: - Cluster topology - Node health dashboard - Process distribution - LiveView message flow """ use Phoenix.LiveView, layout: {DalaDev.Server.Layouts, :app} alias DalaDev.ClusterViz @refresh_interval 5_000 def mount(_params, _session, socket) do if connected?(socket) do :timer.send_interval(@refresh_interval, self(), :refresh) end socket = socket |> assign(:topology, %{}) |> assign(:health, %{}) |> assign(:processes, %{}) |> assign(:flow, %{}) |> assign(:error, nil) {:ok, fetch_all(socket)} end def handle_info(:refresh, socket) do {:noreply, fetch_all(socket)} end def handle_event("refresh", _params, socket) do {:noreply, fetch_all(socket)} end def render(assigns) do ~H"""

Cluster Visualization

<%= if @error do %>

<%= @error %>

<% end %>

Cluster Topology

Node Health

<%= for node_data <- @health[:nodes] || [] do %>

<%= node_data.node %>

(if node_data.status == :alive, do: "bg-green-900 text-green-300", else: "bg-red-900 text-red-300")}> <%= node_data.status %>
Latency
<%= node_data.latency_ms || "N/A" %> ms
Processes
<%= node_data.process_count || "N/A" %>
<%= if node_data.memory do %>
Memory
<%= format_bytes(node_data.memory[:total_memory]) %>
<% end %>
<% end %>

Process Distribution

LiveView Message Flow

""" end defp fetch_all(socket) do socket |> fetch_topology() |> fetch_health() |> fetch_processes() |> fetch_flow() end defp fetch_topology(socket) do case ClusterViz.topology() do {:ok, data} -> assign(socket, :topology, data) {:error, reason} -> assign(socket, :error, "Topology: #{reason}") end end defp fetch_health(socket) do case ClusterViz.health_dashboard() do {:ok, data} -> assign(socket, :health, data) {:error, reason} -> assign(socket, :error, "Health: #{reason}") end end defp fetch_processes(socket) do case ClusterViz.process_distribution() do {:ok, data} -> assign(socket, :processes, data) {:error, reason} -> assign(socket, :error, "Processes: #{reason}") end end defp fetch_flow(socket) do case ClusterViz.liveview_flow() do {:ok, data} -> assign(socket, :flow, data) {:error, reason} -> assign(socket, :error, "Flow: #{reason}") end end defp format_bytes(nil), do: "N/A" defp format_bytes(bytes) when bytes < 1024, do: "#{bytes} B" defp format_bytes(bytes) when bytes < 1024 * 1024, do: "#{Float.round(bytes / 1024, 1)} KB" defp format_bytes(bytes), do: "#{Float.round(bytes / (1024 * 1024), 1)} MB" end