239 lines
9.7 KiB
Text
239 lines
9.7 KiB
Text
<Layouts.authenticated
|
|
flash={@flash}
|
|
current_scope={@current_scope}
|
|
current_organization={@current_organization}
|
|
active_page="agents"
|
|
timezone={@timezone}
|
|
>
|
|
<.header>
|
|
{@page_title}
|
|
<:subtitle>Agent Details and Health</:subtitle>
|
|
<:actions>
|
|
<.link
|
|
navigate={~p"/orgs/#{@current_organization.slug}/agents/#{@agent_token.id}/edit"}
|
|
class="inline-flex items-center gap-2 rounded-lg px-3.5 py-2.5 text-sm font-semibold shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500 dark:bg-blue-500 dark:hover:bg-blue-600"
|
|
>
|
|
<.icon name="hero-pencil" class="h-5 w-5" /> Edit Agent
|
|
</.link>
|
|
<.link
|
|
navigate={~p"/orgs/#{@current_organization.slug}/agents"}
|
|
class="text-sm font-semibold text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white"
|
|
>
|
|
<.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-gray-800/50 border border-gray-200 dark:border-white/10 rounded-lg p-6">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="text-sm font-medium text-gray-600 dark:text-gray-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-gray-400 dark:text-gray-500" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Device Count Card -->
|
|
<div class="bg-white dark:bg-gray-800/50 border border-gray-200 dark:border-white/10 rounded-lg p-6">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Device</p>
|
|
<p class="mt-2 text-3xl font-semibold text-gray-900 dark:text-white">
|
|
{length(@polling_targets)}
|
|
</p>
|
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
{@direct_assignments} direct
|
|
</p>
|
|
</div>
|
|
<.icon name="hero-server" class="h-8 w-8 text-gray-400 dark:text-gray-500" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Last Seen Card -->
|
|
<div class="bg-white dark:bg-gray-800/50 border border-gray-200 dark:border-white/10 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-gray-600 dark:text-gray-400">Last Seen</p>
|
|
<p class="mt-2 text-lg font-semibold text-gray-900 dark:text-white">
|
|
<.timestamp
|
|
datetime={@agent_token.last_seen_at}
|
|
timezone={@timezone}
|
|
/>
|
|
</p>
|
|
<%= if @agent_token.last_seen_at do %>
|
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
<.timestamp
|
|
datetime={@agent_token.last_seen_at}
|
|
timezone={@timezone}
|
|
/>
|
|
</p>
|
|
<% end %>
|
|
<%= if @agent_token.last_ip do %>
|
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400 font-mono">
|
|
{@agent_token.last_ip}
|
|
</p>
|
|
<% end %>
|
|
</div>
|
|
<.icon name="hero-clock" class="h-8 w-8 text-gray-400 dark:text-gray-500 flex-shrink-0" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Uptime Card -->
|
|
<div class="bg-white dark:bg-gray-800/50 border border-gray-200 dark:border-white/10 rounded-lg p-6">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="text-sm font-medium text-gray-600 dark:text-gray-400">Uptime</p>
|
|
<%= if @agent_token.metadata["uptime_seconds"] do %>
|
|
<p class="mt-2 text-lg font-semibold text-gray-900 dark:text-white">
|
|
{format_uptime(@agent_token.metadata["uptime_seconds"])}
|
|
</p>
|
|
<% else %>
|
|
<p class="mt-2 text-lg font-semibold text-gray-500 dark:text-gray-400">
|
|
Unknown
|
|
</p>
|
|
<% end %>
|
|
</div>
|
|
<.icon name="hero-clock" class="h-8 w-8 text-gray-400 dark:text-gray-500" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Agent Metadata -->
|
|
<%= if @agent_token.metadata && map_size(@agent_token.metadata) > 0 do %>
|
|
<div class="mt-6 bg-white dark:bg-gray-800/50 border border-gray-200 dark:border-white/10 rounded-lg p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white 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-gray-600 dark:text-gray-400">Hostname</dt>
|
|
<dd class="mt-1 text-sm text-gray-900 dark:text-white font-mono">
|
|
{@agent_token.metadata["hostname"]}
|
|
</dd>
|
|
</div>
|
|
<% end %>
|
|
<%= if @agent_token.metadata["uptime_seconds"] do %>
|
|
<div>
|
|
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Uptime</dt>
|
|
<dd class="mt-1 text-sm text-gray-900 dark:text-white">
|
|
{format_uptime(@agent_token.metadata["uptime_seconds"])}
|
|
</dd>
|
|
</div>
|
|
<% end %>
|
|
</dl>
|
|
</div>
|
|
<% end %>
|
|
|
|
<!-- Agent Timestamps -->
|
|
<div class="mt-6 bg-white dark:bg-gray-800/50 border border-gray-200 dark:border-white/10 rounded-lg p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
|
Timestamps
|
|
</h3>
|
|
<dl class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
<div>
|
|
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Created</dt>
|
|
<dd class="mt-1 text-sm text-gray-900 dark:text-white">
|
|
<.timestamp datetime={@agent_token.inserted_at} timezone={@timezone} />
|
|
</dd>
|
|
</div>
|
|
|
|
<div>
|
|
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Last Updated</dt>
|
|
<dd class="mt-1 text-sm text-gray-900 dark:text-white">
|
|
<.timestamp datetime={@agent_token.updated_at} timezone={@timezone} />
|
|
</dd>
|
|
</div>
|
|
|
|
<div>
|
|
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">
|
|
Last Seen (Heartbeat)
|
|
</dt>
|
|
<dd class="mt-1 text-sm text-gray-900 dark:text-white">
|
|
<.timestamp datetime={@agent_token.last_seen_at} timezone={@timezone} />
|
|
</dd>
|
|
</div>
|
|
|
|
<%= if @agent_token.last_ip do %>
|
|
<div>
|
|
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Last IP Address</dt>
|
|
<dd class="mt-1 text-sm text-gray-900 dark:text-white font-mono">
|
|
{@agent_token.last_ip}
|
|
</dd>
|
|
</div>
|
|
<% end %>
|
|
</dl>
|
|
</div>
|
|
|
|
<!-- Device List -->
|
|
<div class="mt-6 bg-white dark:bg-gray-800/50 border border-gray-200 dark:border-white/10 rounded-lg">
|
|
<div class="p-6 border-b border-gray-200 dark:border-white/10">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">
|
|
Polling Targets
|
|
</h3>
|
|
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
|
Device 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-gray-400 dark:text-gray-500" />
|
|
<h3 class="mt-4 text-sm font-semibold text-gray-900 dark:text-white">
|
|
No devices assigned
|
|
</h3>
|
|
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
|
|
This agent is not currently polling any device.
|
|
</p>
|
|
</div>
|
|
<% else %>
|
|
<div class="divide-y divide-gray-200 dark:divide-white/10">
|
|
<%= for device <- @polling_targets do %>
|
|
<.link
|
|
navigate={~p"/devices/#{device.id}"}
|
|
class="block hover:bg-gray-50 dark:hover:bg-gray-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-gray-900 dark:text-white truncate">
|
|
{device.name}
|
|
</p>
|
|
<% {source, source_label} = assignment_source(device, @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-gray-600 dark:text-gray-400">
|
|
<span class="flex items-center gap-1">
|
|
<.icon name="hero-globe-alt" class="h-4 w-4" />
|
|
{device.ip_address}
|
|
</span>
|
|
<%= if device.site do %>
|
|
<span class="flex items-center gap-1">
|
|
<.icon name="hero-building-office" class="h-4 w-4" />
|
|
{device.site.name}
|
|
</span>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
<.icon name="hero-chevron-right" class="h-5 w-5 text-gray-400 dark:text-gray-500" />
|
|
</div>
|
|
</div>
|
|
</.link>
|
|
<% end %>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
</Layouts.authenticated>
|