From 8864b8d26d3c5a51ea9b4eafecbe97149980e960 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Wed, 14 Jan 2026 16:20:20 -0600 Subject: [PATCH] Enhance agent assignment UI with clear inheritance indicators - Add override warnings (amber) when site/equipment explicitly override defaults - Show inherited agent names in organization and site forms - Display equipment assignment breakdown in organization settings - Make equipment boxes fully clickable on site page (remove View button) - Add hover effects to equipment boxes for better UX - Consistent icon usage across all assignment levels --- .../live/equipment_live/form.html.heex | 50 ++++++++----------- lib/towerops_web/live/org/settings_live.ex | 4 ++ .../live/org/settings_live.html.heex | 31 +++++++++++- lib/towerops_web/live/site_live/form.ex | 18 +++++++ .../live/site_live/form.html.heex | 21 ++++++-- .../live/site_live/show.html.heex | 32 ++++++------ 6 files changed, 106 insertions(+), 50 deletions(-) diff --git a/lib/towerops_web/live/equipment_live/form.html.heex b/lib/towerops_web/live/equipment_live/form.html.heex index 5d753d5c..c18f445d 100644 --- a/lib/towerops_web/live/equipment_live/form.html.heex +++ b/lib/towerops_web/live/equipment_live/form.html.heex @@ -62,34 +62,28 @@ options={Enum.map(@available_agents, &{&1.name, &1.id})} /> <%= if @live_action == :edit and Map.has_key?(assigns, :agent_source) do %> -

- <%= case @agent_source do %> - <% :equipment -> %> - - <.icon name="hero-server" class="h-4 w-4" /> - Explicitly assigned to this equipment - - <% :site -> %> - - <.icon name="hero-building-office" class="h-4 w-4" /> Inherited from site - <%= if @effective_agent_name do %> - ({@effective_agent_name}) - <% end %> - - <% :organization -> %> - - <.icon name="hero-building-office-2" class="h-4 w-4" /> - Inherited from organization - <%= if @effective_agent_name do %> - ({@effective_agent_name}) - <% end %> - - <% :none -> %> - - <.icon name="hero-cloud" class="h-4 w-4" /> No agent assigned - cloud polling - - <% end %> -

+ <%= case @agent_source do %> + <% :equipment -> %> +

+ <.icon name="hero-exclamation-triangle" class="h-4 w-4" /> + Overriding site/organization defaults - + this equipment is explicitly assigned +

+ <% :site -> %> +

+ <.icon name="hero-building-office" class="h-4 w-4" /> Inherited from site: + {@effective_agent_name} +

+ <% :organization -> %> +

+ <.icon name="hero-building-office-2" class="h-4 w-4" /> + Inherited from organization: {@effective_agent_name} +

+ <% :none -> %> +

+ <.icon name="hero-cloud" class="h-4 w-4" /> No agent assigned - cloud polling +

+ <% end %> <% else %>

Assign this equipment to a remote agent for local SNMP polling. Leave empty to inherit from site or organization defaults. diff --git a/lib/towerops_web/live/org/settings_live.ex b/lib/towerops_web/live/org/settings_live.ex index f502bbfb..88ac4d79 100644 --- a/lib/towerops_web/live/org/settings_live.ex +++ b/lib/towerops_web/live/org/settings_live.ex @@ -10,12 +10,16 @@ defmodule ToweropsWeb.Org.SettingsLive do organization = socket.assigns.current_organization available_agents = Agents.list_organization_agent_tokens(organization.id) + # Get assignment breakdown to show impact + assignment_breakdown = Agents.Stats.get_equipment_assignment_breakdown(organization.id) + changeset = Organizations.change_organization(organization) {:ok, socket |> assign(:organization, organization) |> assign(:available_agents, available_agents) + |> assign(:assignment_breakdown, assignment_breakdown) |> assign(:form, to_form(changeset))} end diff --git a/lib/towerops_web/live/org/settings_live.html.heex b/lib/towerops_web/live/org/settings_live.html.heex index 028eae33..354a1376 100644 --- a/lib/towerops_web/live/org/settings_live.html.heex +++ b/lib/towerops_web/live/org/settings_live.html.heex @@ -38,7 +38,36 @@ options={Enum.map(@available_agents, &{&1.name, &1.id})} /> -

+

+

+ Current Equipment Assignment: +

+
+
+ <.icon name="hero-server" class="h-4 w-4" /> + {@assignment_breakdown.direct} explicitly assigned +
+
+ <.icon name="hero-building-office" class="h-4 w-4" /> + {@assignment_breakdown.site} inherited from site +
+
+ <.icon name="hero-building-office-2" class="h-4 w-4" /> + + {@assignment_breakdown.organization} + inheriting organization default + +
+
+ <.icon name="hero-cloud" class="h-4 w-4" /> + + {@assignment_breakdown.cloud} cloud polling (no agent) + +
+
+
+ +

<.icon name="hero-information-circle" class="h-4 w-4 inline" /> Agent assignment hierarchy: Equipment > Site > Organization

diff --git a/lib/towerops_web/live/site_live/form.ex b/lib/towerops_web/live/site_live/form.ex index 0d2f363f..941d221d 100644 --- a/lib/towerops_web/live/site_live/form.ex +++ b/lib/towerops_web/live/site_live/form.ex @@ -25,10 +25,19 @@ defmodule ToweropsWeb.SiteLive.Form do defp apply_action(socket, :new, _params) do changeset = Sites.change_site(%Site{organization_id: socket.assigns.organization.id}) + # Get inherited agent info from organization + org_agent = + if socket.assigns.organization.default_agent_token_id do + Enum.find(socket.assigns.available_agents, fn a -> + a.id == socket.assigns.organization.default_agent_token_id + end) + end + socket |> assign(:page_title, "New Site") |> assign(:site, %Site{}) |> assign(:form, to_form(changeset)) + |> assign(:org_agent, org_agent) end defp apply_action(socket, :edit, %{"id" => id}) do @@ -39,11 +48,20 @@ defmodule ToweropsWeb.SiteLive.Form do available_parent_sites = Enum.reject(socket.assigns.available_parent_sites, fn s -> s.id == site.id end) + # Get inherited agent info from organization + org_agent = + if socket.assigns.organization.default_agent_token_id do + Enum.find(socket.assigns.available_agents, fn a -> + a.id == socket.assigns.organization.default_agent_token_id + end) + end + socket |> assign(:page_title, "Edit Site") |> assign(:site, site) |> assign(:form, to_form(changeset)) |> assign(:available_parent_sites, available_parent_sites) + |> assign(:org_agent, org_agent) end @impl true diff --git a/lib/towerops_web/live/site_live/form.html.heex b/lib/towerops_web/live/site_live/form.html.heex index ed2026a5..8fb77aa1 100644 --- a/lib/towerops_web/live/site_live/form.html.heex +++ b/lib/towerops_web/live/site_live/form.html.heex @@ -48,9 +48,24 @@ prompt="Inherit from organization" options={Enum.map(@available_agents, &{&1.name, &1.id})} /> -

- Set a default agent for all equipment at this site. Equipment can override this setting individually. -

+ <%= if @live_action == :edit and @site.agent_token_id do %> +

+ <.icon name="hero-exclamation-triangle" class="h-4 w-4" /> + Overriding organization default - + all equipment at this site will use this agent +

+ <% else %> + <%= if Map.get(assigns, :org_agent) do %> +

+ <.icon name="hero-building-office-2" class="h-4 w-4" /> + Currently inheriting organization default: {@org_agent.name} +

+ <% else %> +

+ Set a default agent for all equipment at this site. Equipment can override this setting individually. +

+ <% end %> + <% end %> <% end %> <.input field={@form[:description]} type="textarea" label="Description" /> diff --git a/lib/towerops_web/live/site_live/show.html.heex b/lib/towerops_web/live/site_live/show.html.heex index 60e7099c..e3845728 100644 --- a/lib/towerops_web/live/site_live/show.html.heex +++ b/lib/towerops_web/live/site_live/show.html.heex @@ -96,30 +96,26 @@ <% else %>
-

{eq.name}

{eq.ip_address}

-
- - {eq.status |> to_string() |> String.upcase()} - - <.button navigate={~p"/orgs/#{@current_organization.slug}/equipment/#{eq.id}"}> - View - -
-
+ + {eq.status |> to_string() |> String.upcase()} + +
<% end %>