defmodule ExESDBDashboard.ClusterComponent do @moduledoc """ LiveComponent for cluster view that can be embedded within other LiveViews. This provides a reusable cluster dashboard component with real-time updates via PubSub integration. """ use Phoenix.LiveComponent @impl true def mount(socket) do {:ok, socket} end @impl true def update(assigns, socket) do socket = socket |> assign(assigns) |> assign_new(:cluster_data, fn -> get_cluster_data() end) |> assign_new(:last_updated, fn -> DateTime.utc_now() end) {:ok, socket} end @impl true def handle_event("refresh", _params, socket) do cluster_data = get_cluster_data() socket = socket |> assign(:cluster_data, cluster_data) |> assign(:last_updated, DateTime.utc_now()) # Send update to parent LiveView send(self(), {:cluster_refreshed, cluster_data}) {:noreply, socket} end @impl true def render(assigns) do ~H"""

🏢 Cluster Overview

Real-time cluster monitoring

📊 Cluster Health

<%= health_icon(@cluster_data.cluster_health) %> <%= health_text(@cluster_data.cluster_health) %>

🖥️ Cluster Nodes

<%= for node <- @cluster_data.nodes do %>
<%= node.name %> <%= node_status_text(node.status) %>
Type: <%= if node.is_ex_esdb, do: "ExESDB", else: "Gater" %>
Uptime: <%= format_uptime(node.uptime) %>
Last seen: <%= format_datetime(node.last_seen) %>
<% end %>

🏪 Data Stores

<%= for store <- @cluster_data.stores do %>
<%= store.name %> <%= store.status %>
<%= store.stream_count %> Streams
<%= store.subscription_count %> Subscriptions
<%= length(store.nodes) %> Nodes
<% end %>

📈 Cluster Summary

<%= length(@cluster_data.nodes) %> Total Nodes
<%= length(@cluster_data.stores) %> Data Stores
<%= @cluster_data.total_streams %> Total Streams
<%= format_datetime(@cluster_data.updated_at) %> Last Updated

🕐 Last updated: <%= format_datetime(@last_updated) %>

""" end # Private helper functions defp get_cluster_data do try do ExESDBDashboard.get_cluster_data() rescue _ -> # Fallback data if ExESDBDashboard is not available %{ nodes: [%{name: Node.self(), status: :self, is_ex_esdb: false, uptime: 0, last_seen: DateTime.utc_now()}], stores: [], total_streams: 0, cluster_health: :no_cluster, updated_at: DateTime.utc_now() } end end defp health_status_class(health) do case health do :healthy -> "healthy" :degraded -> "degraded" :unhealthy -> "unhealthy" :no_cluster -> "no-cluster" :no_stores -> "no-stores" _ -> "unknown" end end defp health_icon(health) do case health do :healthy -> "✅" :degraded -> "⚠️" :unhealthy -> "❌" :no_cluster -> "🔍" :no_stores -> "📦" _ -> "❓" end end defp health_text(health) do case health do :healthy -> "All systems operational" :degraded -> "Some issues detected" :unhealthy -> "Critical issues found" :no_cluster -> "No cluster detected" :no_stores -> "No data stores available" _ -> "Health status unknown" end end defp node_status_class(status) do case status do :connected -> "connected" :self -> "self" _ -> "unknown" end end defp node_status_text(status) do case status do :connected -> "Connected" :self -> "Local Node" _ -> "Unknown" end end defp store_status_class(status) do case status do :healthy -> "healthy" :degraded -> "degraded" :unhealthy -> "unhealthy" _ -> "unknown" end end defp format_uptime(uptime_ms) when is_integer(uptime_ms) do seconds = div(uptime_ms, 1000) cond do seconds < 60 -> "#{seconds}s" seconds < 3600 -> "#{div(seconds, 60)}m" seconds < 86400 -> "#{div(seconds, 3600)}h" true -> "#{div(seconds, 86400)}d" end end defp format_uptime(_), do: "unknown" defp format_datetime(%DateTime{} = dt) do Calendar.strftime(dt, "%H:%M:%S") end defp format_datetime(_), do: "unknown" end