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
This commit is contained in:
parent
8442d9e1fc
commit
8864b8d26d
6 changed files with 106 additions and 50 deletions
|
|
@ -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 %>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
<%= case @agent_source do %>
|
||||
<% :equipment -> %>
|
||||
<span class="inline-flex items-center gap-1">
|
||||
<.icon name="hero-server" class="h-4 w-4" />
|
||||
Explicitly assigned to this equipment
|
||||
</span>
|
||||
<% :site -> %>
|
||||
<span class="inline-flex items-center gap-1">
|
||||
<.icon name="hero-building-office" class="h-4 w-4" /> Inherited from site
|
||||
<%= if @effective_agent_name do %>
|
||||
(<strong>{@effective_agent_name}</strong>)
|
||||
<% end %>
|
||||
</span>
|
||||
<% :organization -> %>
|
||||
<span class="inline-flex items-center gap-1">
|
||||
<.icon name="hero-building-office-2" class="h-4 w-4" />
|
||||
Inherited from organization
|
||||
<%= if @effective_agent_name do %>
|
||||
(<strong>{@effective_agent_name}</strong>)
|
||||
<% end %>
|
||||
</span>
|
||||
<% :none -> %>
|
||||
<span class="inline-flex items-center gap-1">
|
||||
<.icon name="hero-cloud" class="h-4 w-4" /> No agent assigned - cloud polling
|
||||
</span>
|
||||
<% end %>
|
||||
</p>
|
||||
<%= case @agent_source do %>
|
||||
<% :equipment -> %>
|
||||
<p class="mt-1 text-sm text-amber-600 dark:text-amber-400 flex items-center gap-1">
|
||||
<.icon name="hero-exclamation-triangle" class="h-4 w-4" />
|
||||
<strong>Overriding</strong> site/organization defaults -
|
||||
this equipment is explicitly assigned
|
||||
</p>
|
||||
<% :site -> %>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400 flex items-center gap-1">
|
||||
<.icon name="hero-building-office" class="h-4 w-4" /> Inherited from site:
|
||||
<strong>{@effective_agent_name}</strong>
|
||||
</p>
|
||||
<% :organization -> %>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400 flex items-center gap-1">
|
||||
<.icon name="hero-building-office-2" class="h-4 w-4" />
|
||||
Inherited from organization: <strong>{@effective_agent_name}</strong>
|
||||
</p>
|
||||
<% :none -> %>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400 flex items-center gap-1">
|
||||
<.icon name="hero-cloud" class="h-4 w-4" /> No agent assigned - cloud polling
|
||||
</p>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Assign this equipment to a remote agent for local SNMP polling. Leave empty to inherit from site or organization defaults.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,36 @@
|
|||
options={Enum.map(@available_agents, &{&1.name, &1.id})}
|
||||
/>
|
||||
|
||||
<p class="mt-2 text-sm text-zinc-500 dark:text-zinc-400 italic">
|
||||
<div class="mt-3 p-3 bg-zinc-50 dark:bg-zinc-800 rounded-lg text-sm">
|
||||
<p class="font-medium text-zinc-700 dark:text-zinc-300 mb-2">
|
||||
Current Equipment Assignment:
|
||||
</p>
|
||||
<div class="space-y-1 text-zinc-600 dark:text-zinc-400">
|
||||
<div class="flex items-center gap-2">
|
||||
<.icon name="hero-server" class="h-4 w-4" />
|
||||
<span><strong>{@assignment_breakdown.direct}</strong> explicitly assigned</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<.icon name="hero-building-office" class="h-4 w-4" />
|
||||
<span><strong>{@assignment_breakdown.site}</strong> inherited from site</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<.icon name="hero-building-office-2" class="h-4 w-4" />
|
||||
<span>
|
||||
<strong>{@assignment_breakdown.organization}</strong>
|
||||
inheriting organization default
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<.icon name="hero-cloud" class="h-4 w-4" />
|
||||
<span>
|
||||
<strong>{@assignment_breakdown.cloud}</strong> cloud polling (no agent)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="mt-3 text-sm text-zinc-500 dark:text-zinc-400 italic">
|
||||
<.icon name="hero-information-circle" class="h-4 w-4 inline" />
|
||||
Agent assignment hierarchy: Equipment > Site > Organization
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -48,9 +48,24 @@
|
|||
prompt="Inherit from organization"
|
||||
options={Enum.map(@available_agents, &{&1.name, &1.id})}
|
||||
/>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Set a default agent for all equipment at this site. Equipment can override this setting individually.
|
||||
</p>
|
||||
<%= if @live_action == :edit and @site.agent_token_id do %>
|
||||
<p class="mt-1 text-sm text-amber-600 dark:text-amber-400 flex items-center gap-1">
|
||||
<.icon name="hero-exclamation-triangle" class="h-4 w-4" />
|
||||
<strong>Overriding</strong> organization default -
|
||||
all equipment at this site will use this agent
|
||||
</p>
|
||||
<% else %>
|
||||
<%= if Map.get(assigns, :org_agent) do %>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400 flex items-center gap-1">
|
||||
<.icon name="hero-building-office-2" class="h-4 w-4" />
|
||||
Currently inheriting organization default: <strong>{@org_agent.name}</strong>
|
||||
</p>
|
||||
<% else %>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Set a default agent for all equipment at this site. Equipment can override this setting individually.
|
||||
</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<.input field={@form[:description]} type="textarea" label="Description" />
|
||||
|
|
|
|||
|
|
@ -96,30 +96,26 @@
|
|||
</div>
|
||||
<% else %>
|
||||
<div class="space-y-3">
|
||||
<div
|
||||
<.link
|
||||
:for={eq <- @equipment}
|
||||
class="flex items-center justify-between rounded-lg border border-zinc-200 bg-white p-4 dark:border-zinc-800 dark:bg-zinc-900"
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/equipment/#{eq.id}"}
|
||||
class="flex items-center justify-between rounded-lg border border-zinc-200 bg-white p-4 dark:border-zinc-800 dark:bg-zinc-900 hover:border-zinc-300 dark:hover:border-zinc-700 hover:bg-zinc-50 dark:hover:bg-zinc-800 transition-colors cursor-pointer"
|
||||
>
|
||||
<div>
|
||||
<p class="font-medium text-zinc-900 dark:text-zinc-100">{eq.name}</p>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-400">{eq.ip_address}</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class={[
|
||||
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
|
||||
eq.status == :up &&
|
||||
"bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200",
|
||||
eq.status == :down && "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200",
|
||||
eq.status == :unknown &&
|
||||
"bg-zinc-100 text-zinc-800 dark:bg-zinc-800 dark:text-zinc-200"
|
||||
]}>
|
||||
{eq.status |> to_string() |> String.upcase()}
|
||||
</span>
|
||||
<.button navigate={~p"/orgs/#{@current_organization.slug}/equipment/#{eq.id}"}>
|
||||
View
|
||||
</.button>
|
||||
</div>
|
||||
</div>
|
||||
<span class={[
|
||||
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
|
||||
eq.status == :up &&
|
||||
"bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200",
|
||||
eq.status == :down && "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200",
|
||||
eq.status == :unknown &&
|
||||
"bg-zinc-100 text-zinc-800 dark:bg-zinc-800 dark:text-zinc-200"
|
||||
]}>
|
||||
{eq.status |> to_string() |> String.upcase()}
|
||||
</span>
|
||||
</.link>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue