defmodule ToweropsWeb.NetworkMapLive do @moduledoc false use ToweropsWeb, :live_view alias Towerops.Topology @impl true def mount(_params, _session, socket) do organization = socket.assigns.current_scope.organization # Subscribe to topology changes if connected _ = if connected?(socket) do Phoenix.PubSub.subscribe(Towerops.PubSub, "topology:#{organization.id}") end # Default empty topology default_topology = %{ nodes: [], edges: [], stats: %{ total_devices: 0, added_devices: 0, discovered_devices: 0, total_links: 0 }, last_updated: DateTime.utc_now() } {:ok, socket |> assign(:page_title, t("Network Map")) |> assign(:timezone, socket.assigns.current_scope.timezone) |> assign(:loading, true) |> assign(:topology, default_topology) |> assign(:active_tab, "added") |> assign(:selected_node_detail, nil)} end @impl true def handle_params(params, _url, socket) do tab = Map.get(params, "tab", "added") organization = socket.assigns.current_scope.organization socket = socket |> assign(:active_tab, tab) |> load_topology_data(organization.id, tab) # Handle ?node= param for deep-linking to a selected node socket = case Map.get(params, "node") do nil -> socket node_id -> load_node_detail(socket, node_id, organization.id) end {:noreply, socket} end @impl true def handle_event("refresh_topology", _params, socket) do organization = socket.assigns.current_scope.organization {:noreply, socket |> assign(:loading, true) |> load_topology_data(organization.id, socket.assigns.active_tab) |> put_flash(:info, t("Network map refreshed"))} end @impl true def handle_event("node_clicked", %{"node_id" => node_id} = params, socket) do organization = socket.assigns.current_scope.organization node_type = Map.get(params, "node_type", "managed") socket = socket |> load_node_detail(node_id, organization.id) |> then(fn s -> if s.assigns.selected_node_detail do push_event(s, "highlight_node", %{node_id: node_id, node_type: node_type}) else s end end) {:noreply, socket} end @impl true def handle_event("close_detail_panel", _params, socket) do {:noreply, socket |> assign(:selected_node_detail, nil) |> push_event("clear_highlight", %{})} end @impl true def handle_info({:topology_updated, _organization_id}, socket) do # Real-time topology update {:noreply, load_topology_data( socket, socket.assigns.current_scope.organization.id, socket.assigns.active_tab )} end defp load_topology_data(socket, organization_id, tab) do topology = Topology.get_topology_for_map(organization_id, tab) socket |> assign(:topology, topology) |> assign(:loading, false) |> push_event("update_topology", %{topology: topology}) end defp load_node_detail(socket, node_id, organization_id) do case Topology.get_node_detail(node_id, organization_id) do nil -> assign(socket, :selected_node_detail, nil) detail -> assign(socket, :selected_node_detail, detail) end end end