towerops/lib/towerops_web/live/agent_live/show.html.heex

250 lines
10 KiB
Text

<Layouts.authenticated
flash={@flash}
current_scope={@current_scope}
current_organization={@current_organization}
active_page="agents"
>
<.header>
{@page_title}
<:subtitle>Agent Details and Health</:subtitle>
<:actions>
<.link
navigate={~p"/orgs/#{@current_organization.slug}/agents"}
class="text-sm font-semibold text-zinc-700 dark:text-zinc-300 hover:text-zinc-900 dark:hover:text-zinc-100"
>
<.icon name="hero-arrow-left" class="h-5 w-5" /> Back to Agents
</.link>
</:actions>
</.header>
<!-- Agent Status Overview -->
<div class="mt-6 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4">
<!-- Status Card -->
<div class="bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Status</p>
<% {status, label} = agent_status(@agent_token) %>
<div class="mt-2">
<span class={"inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium #{status_badge_class(status)}"}>
<span class={"h-1.5 w-1.5 rounded-full #{status_dot_class(status)}"} />
{label}
</span>
</div>
</div>
<.icon name="hero-signal" class="h-8 w-8 text-zinc-400 dark:text-zinc-600" />
</div>
</div>
<!-- Equipment Count Card -->
<div class="bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Equipment</p>
<p class="mt-2 text-3xl font-semibold text-zinc-900 dark:text-zinc-100">
{length(@polling_targets)}
</p>
<p class="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
{@direct_assignments} direct
</p>
</div>
<.icon name="hero-server" class="h-8 w-8 text-zinc-400 dark:text-zinc-600" />
</div>
</div>
<!-- Last Seen Card -->
<div class="bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg p-6">
<div class="flex items-center justify-between">
<div class="min-w-0 flex-1">
<p class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Last Seen</p>
<p class="mt-2 text-lg font-semibold text-zinc-900 dark:text-zinc-100">
{format_last_seen(@agent_token.last_seen_at)}
</p>
<%= if @agent_token.last_seen_at do %>
<p class="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
{format_datetime(@agent_token.last_seen_at)}
</p>
<% end %>
<%= if @agent_token.last_ip do %>
<p class="mt-1 text-xs text-zinc-500 dark:text-zinc-400 font-mono">
{@agent_token.last_ip}
</p>
<% end %>
</div>
<.icon name="hero-clock" class="h-8 w-8 text-zinc-400 dark:text-zinc-600 flex-shrink-0" />
</div>
</div>
<!-- Version/Uptime Card -->
<div class="bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Agent Info</p>
<%= if @agent_token.metadata["version"] do %>
<p class="mt-2 text-lg font-semibold text-zinc-900 dark:text-zinc-100">
v{@agent_token.metadata["version"]}
</p>
<% else %>
<p class="mt-2 text-lg font-semibold text-zinc-500 dark:text-zinc-400">
Unknown
</p>
<% end %>
<%= if @agent_token.metadata["uptime_seconds"] do %>
<p class="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
Uptime: {format_uptime(@agent_token.metadata["uptime_seconds"])}
</p>
<% end %>
</div>
<.icon name="hero-information-circle" class="h-8 w-8 text-zinc-400 dark:text-zinc-600" />
</div>
</div>
</div>
<!-- Agent Metadata -->
<%= if @agent_token.metadata && map_size(@agent_token.metadata) > 0 do %>
<div class="mt-6 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg p-6">
<h3 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100 mb-4">
Agent Metadata
</h3>
<dl class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<%= if @agent_token.metadata["hostname"] do %>
<div>
<dt class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Hostname</dt>
<dd class="mt-1 text-sm text-zinc-900 dark:text-zinc-100 font-mono">
{@agent_token.metadata["hostname"]}
</dd>
</div>
<% end %>
<%= if @agent_token.metadata["version"] do %>
<div>
<dt class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Version</dt>
<dd class="mt-1 text-sm text-zinc-900 dark:text-zinc-100">
{@agent_token.metadata["version"]}
</dd>
</div>
<% end %>
<%= if @agent_token.metadata["uptime_seconds"] do %>
<div>
<dt class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Uptime</dt>
<dd class="mt-1 text-sm text-zinc-900 dark:text-zinc-100">
{format_uptime(@agent_token.metadata["uptime_seconds"])}
</dd>
</div>
<% end %>
</dl>
</div>
<% end %>
<!-- Agent Timestamps -->
<div class="mt-6 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg p-6">
<h3 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100 mb-4">
Timestamps
</h3>
<dl class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<dt class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Created</dt>
<dd class="mt-1 text-sm text-zinc-900 dark:text-zinc-100">
{format_datetime(@agent_token.inserted_at)}
</dd>
<dd class="mt-0.5 text-xs text-zinc-500 dark:text-zinc-400">
{format_last_seen(@agent_token.inserted_at)} (created)
</dd>
</div>
<div>
<dt class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Last Updated</dt>
<dd class="mt-1 text-sm text-zinc-900 dark:text-zinc-100">
{format_datetime(@agent_token.updated_at)}
</dd>
<dd class="mt-0.5 text-xs text-zinc-500 dark:text-zinc-400">
{format_last_seen(@agent_token.updated_at)}
</dd>
</div>
<div>
<dt class="text-sm font-medium text-zinc-600 dark:text-zinc-400">
Last Seen (Heartbeat)
</dt>
<dd class="mt-1 text-sm text-zinc-900 dark:text-zinc-100">
{format_datetime(@agent_token.last_seen_at)}
</dd>
<%= if @agent_token.last_seen_at do %>
<dd class="mt-0.5 text-xs text-zinc-500 dark:text-zinc-400">
{format_last_seen(@agent_token.last_seen_at)}
</dd>
<% end %>
</div>
<%= if @agent_token.last_ip do %>
<div>
<dt class="text-sm font-medium text-zinc-600 dark:text-zinc-400">Last IP Address</dt>
<dd class="mt-1 text-sm text-zinc-900 dark:text-zinc-100 font-mono">
{@agent_token.last_ip}
</dd>
</div>
<% end %>
</dl>
</div>
<!-- Equipment List -->
<div class="mt-6 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg">
<div class="p-6 border-b border-zinc-200 dark:border-zinc-800">
<h3 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100">
Polling Targets
</h3>
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
Equipment that this agent is responsible for polling
</p>
</div>
<%= if @polling_targets == [] do %>
<div class="p-12 text-center">
<.icon name="hero-server" class="mx-auto h-12 w-12 text-zinc-400 dark:text-zinc-600" />
<h3 class="mt-4 text-sm font-semibold text-zinc-900 dark:text-zinc-100">
No equipment assigned
</h3>
<p class="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
This agent is not currently polling any equipment.
</p>
</div>
<% else %>
<div class="divide-y divide-zinc-200 dark:divide-zinc-800">
<%= for equipment <- @polling_targets do %>
<.link
navigate={~p"/orgs/#{@current_organization.slug}/equipment/#{equipment.id}"}
class="block hover:bg-zinc-50 dark:hover:bg-zinc-800 transition-colors"
>
<div class="p-6">
<div class="flex items-center justify-between">
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3">
<p class="text-sm font-medium text-zinc-900 dark:text-zinc-100 truncate">
{equipment.name}
</p>
<% {source, source_label} = assignment_source(equipment, @agent_token.id) %>
<span class={"inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium #{source_badge_class(source)}"}>
{source_label}
</span>
</div>
<div class="mt-1 flex items-center gap-4 text-sm text-zinc-600 dark:text-zinc-400">
<span class="flex items-center gap-1">
<.icon name="hero-globe-alt" class="h-4 w-4" />
{equipment.ip_address}
</span>
<%= if equipment.site do %>
<span class="flex items-center gap-1">
<.icon name="hero-building-office" class="h-4 w-4" />
{equipment.site.name}
</span>
<% end %>
</div>
</div>
<.icon name="hero-chevron-right" class="h-5 w-5 text-zinc-400 dark:text-zinc-600" />
</div>
</div>
</.link>
<% end %>
</div>
<% end %>
</div>
</Layouts.authenticated>