Add onboarding wizard to guide users through site creation
- Dashboard shows welcome wizard when no sites exist - Equipment page redirects to site creation when no sites exist - Equipment form redirects users to create a site if none exist - Equipment list hides "New Equipment" button when no sites exist - Update layouts to accept nil current_organization for org-less pages
This commit is contained in:
parent
6bc802b22a
commit
e3c3056aca
5 changed files with 286 additions and 188 deletions
|
|
@ -59,9 +59,9 @@ defmodule ToweropsWeb.Layouts do
|
|||
"""
|
||||
attr :flash, :map, required: true, doc: "the map of flash messages"
|
||||
|
||||
attr :current_organization, :map,
|
||||
attr :current_organization, :any,
|
||||
default: nil,
|
||||
doc: "the current organization"
|
||||
doc: "the current organization (can be nil for pages without org context)"
|
||||
|
||||
attr :active_page, :string,
|
||||
default: nil,
|
||||
|
|
|
|||
|
|
@ -8,142 +8,201 @@
|
|||
<:subtitle>Welcome to {@current_organization.name}</:subtitle>
|
||||
</.header>
|
||||
|
||||
<div class="mt-8 grid gap-6 md:grid-cols-2 lg:grid-cols-4">
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/sites"}
|
||||
class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900"
|
||||
>
|
||||
<h3 class="text-sm font-medium text-zinc-500 dark:text-zinc-400">Sites</h3>
|
||||
<p class="mt-2 text-3xl font-bold text-zinc-900 dark:text-zinc-100">{@sites_count}</p>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">Total sites</p>
|
||||
</.link>
|
||||
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/equipment"}
|
||||
class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900"
|
||||
>
|
||||
<h3 class="text-sm font-medium text-zinc-500 dark:text-zinc-400">Equipment</h3>
|
||||
<p class="mt-2 text-3xl font-bold text-zinc-900 dark:text-zinc-100">{@equipment_count}</p>
|
||||
<div class="mt-3 space-y-1.5 text-sm">
|
||||
<div class="flex items-center gap-2 text-zinc-700 dark:text-zinc-300">
|
||||
<span class="h-2 w-2 rounded-full bg-green-500"></span>
|
||||
{@equipment_up} Up
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-zinc-700 dark:text-zinc-300">
|
||||
<span class="h-2 w-2 rounded-full bg-red-500"></span>
|
||||
{@equipment_down} Down
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-zinc-700 dark:text-zinc-300">
|
||||
<span class="h-2 w-2 rounded-full bg-zinc-400"></span>
|
||||
{@equipment_unknown} Unknown
|
||||
<%= if @sites_count == 0 do %>
|
||||
<div class="mt-8 rounded-lg border-2 border-dashed border-zinc-300 bg-zinc-50 p-12 text-center dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<.icon
|
||||
name="hero-light-bulb"
|
||||
class="mx-auto h-16 w-16 text-blue-500 dark:text-blue-400"
|
||||
/>
|
||||
<h3 class="mt-6 text-xl font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
Let's get started!
|
||||
</h3>
|
||||
<p class="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Follow these steps to set up your network monitoring:
|
||||
</p>
|
||||
<div class="mt-8 max-w-2xl mx-auto">
|
||||
<div class="space-y-4 text-left">
|
||||
<div class="flex gap-4 items-start">
|
||||
<div class="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-blue-100 text-blue-600 font-semibold dark:bg-blue-900 dark:text-blue-300">
|
||||
1
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-medium text-zinc-900 dark:text-zinc-100">Create a Site</h4>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Sites represent your physical locations (offices, data centers, towers, etc.)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-4 items-start">
|
||||
<div class="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-zinc-200 text-zinc-600 font-semibold dark:bg-zinc-700 dark:text-zinc-400">
|
||||
2
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-medium text-zinc-900 dark:text-zinc-100">Add Equipment</h4>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Add network devices (routers, switches, servers) to your sites
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-4 items-start">
|
||||
<div class="flex-shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-zinc-200 text-zinc-600 font-semibold dark:bg-zinc-700 dark:text-zinc-400">
|
||||
3
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-medium text-zinc-900 dark:text-zinc-100">
|
||||
Monitor & Receive Alerts
|
||||
</h4>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Towerops will automatically monitor your equipment and alert you of issues
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</.link>
|
||||
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/alerts"}
|
||||
class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900"
|
||||
>
|
||||
<h3 class="text-sm font-medium text-zinc-500 dark:text-zinc-400">Active Alerts</h3>
|
||||
<p class={[
|
||||
"mt-2 text-3xl font-bold",
|
||||
(length(@active_alerts) > 0 && "text-red-600 dark:text-red-500") ||
|
||||
"text-zinc-900 dark:text-zinc-100"
|
||||
]}>
|
||||
{length(@active_alerts)}
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">Requires attention</p>
|
||||
</.link>
|
||||
|
||||
<div class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<h3 class="text-sm font-medium text-zinc-500 dark:text-zinc-400">System Status</h3>
|
||||
<div class={[
|
||||
"mt-3 flex items-center gap-2 text-sm font-semibold",
|
||||
(@equipment_down == 0 && "text-green-600 dark:text-green-500") ||
|
||||
"text-amber-600 dark:text-amber-500"
|
||||
]}>
|
||||
<%= if @equipment_down == 0 do %>
|
||||
<.icon name="hero-check-circle" class="h-5 w-5" />
|
||||
<span>All Systems Operational</span>
|
||||
<% else %>
|
||||
<.icon name="hero-exclamation-triangle" class="h-5 w-5" />
|
||||
<span>{@equipment_down} Equipment Down</span>
|
||||
<% end %>
|
||||
<div class="mt-8">
|
||||
<.button navigate={~p"/orgs/#{@current_organization.slug}/sites/new"} variant="primary">
|
||||
<.icon name="hero-plus" class="h-5 w-5" /> Create Your First Site
|
||||
</.button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="mt-8 grid gap-6 md:grid-cols-2 lg:grid-cols-4">
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/sites"}
|
||||
class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900"
|
||||
>
|
||||
<h3 class="text-sm font-medium text-zinc-500 dark:text-zinc-400">Sites</h3>
|
||||
<p class="mt-2 text-3xl font-bold text-zinc-900 dark:text-zinc-100">{@sites_count}</p>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">Total sites</p>
|
||||
</.link>
|
||||
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/equipment"}
|
||||
class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900"
|
||||
>
|
||||
<h3 class="text-sm font-medium text-zinc-500 dark:text-zinc-400">Equipment</h3>
|
||||
<p class="mt-2 text-3xl font-bold text-zinc-900 dark:text-zinc-100">{@equipment_count}</p>
|
||||
<div class="mt-3 space-y-1.5 text-sm">
|
||||
<div class="flex items-center gap-2 text-zinc-700 dark:text-zinc-300">
|
||||
<span class="h-2 w-2 rounded-full bg-green-500"></span>
|
||||
{@equipment_up} Up
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-zinc-700 dark:text-zinc-300">
|
||||
<span class="h-2 w-2 rounded-full bg-red-500"></span>
|
||||
{@equipment_down} Down
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-zinc-700 dark:text-zinc-300">
|
||||
<span class="h-2 w-2 rounded-full bg-zinc-400"></span>
|
||||
{@equipment_unknown} Unknown
|
||||
</div>
|
||||
</div>
|
||||
</.link>
|
||||
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/alerts"}
|
||||
class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900"
|
||||
>
|
||||
<h3 class="text-sm font-medium text-zinc-500 dark:text-zinc-400">Active Alerts</h3>
|
||||
<p class={[
|
||||
"mt-2 text-3xl font-bold",
|
||||
(length(@active_alerts) > 0 && "text-red-600 dark:text-red-500") ||
|
||||
"text-zinc-900 dark:text-zinc-100"
|
||||
]}>
|
||||
{length(@active_alerts)}
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">Requires attention</p>
|
||||
</.link>
|
||||
|
||||
<div class="rounded-lg border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<h3 class="text-sm font-medium text-zinc-500 dark:text-zinc-400">System Status</h3>
|
||||
<div class={[
|
||||
"mt-3 flex items-center gap-2 text-sm font-semibold",
|
||||
(@equipment_down == 0 && "text-green-600 dark:text-green-500") ||
|
||||
"text-amber-600 dark:text-amber-500"
|
||||
]}>
|
||||
<%= if @equipment_down == 0 do %>
|
||||
<.icon name="hero-check-circle" class="h-5 w-5" />
|
||||
<span>All Systems Operational</span>
|
||||
<% else %>
|
||||
<.icon name="hero-exclamation-triangle" class="h-5 w-5" />
|
||||
<span>{@equipment_down} Equipment Down</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= if length(@active_alerts) > 0 do %>
|
||||
<div class="mt-8">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-xl font-semibold text-zinc-900 dark:text-zinc-100">Active Alerts</h2>
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/alerts"}
|
||||
class="text-sm font-medium text-blue-600 hover:text-blue-700 dark:text-blue-500 dark:hover:text-blue-400"
|
||||
>
|
||||
View All Alerts →
|
||||
</.link>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
<%= for alert <- Enum.take(@active_alerts, 5) do %>
|
||||
<div class="flex items-start gap-3 rounded-lg border border-red-200 bg-red-50 p-4 dark:border-red-900/50 dark:bg-red-950/30">
|
||||
<.icon
|
||||
name="hero-exclamation-triangle"
|
||||
class="h-5 w-5 flex-shrink-0 text-red-600 dark:text-red-500"
|
||||
/>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
<.link
|
||||
navigate={
|
||||
~p"/orgs/#{@current_organization.slug}/equipment/#{alert.equipment.id}"
|
||||
}
|
||||
class="hover:text-blue-600 hover:underline dark:hover:text-blue-400"
|
||||
>
|
||||
{alert.equipment.name}
|
||||
</.link>
|
||||
</h3>
|
||||
<p class="mt-0.5 text-sm text-zinc-700 dark:text-zinc-300">{alert.message}</p>
|
||||
</div>
|
||||
<span class="text-xs text-zinc-500 dark:text-zinc-400 whitespace-nowrap">
|
||||
{Calendar.strftime(alert.triggered_at, "%H:%M")}
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if length(@active_alerts) > 5 do %>
|
||||
<div class="text-center text-sm text-zinc-500 dark:text-zinc-400">
|
||||
+ {length(@active_alerts) - 5} more alerts
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if length(@active_alerts) > 0 do %>
|
||||
<div class="mt-8">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-xl font-semibold text-zinc-900 dark:text-zinc-100">Active Alerts</h2>
|
||||
<h2 class="text-xl font-semibold mb-4 text-zinc-900 dark:text-zinc-100">Quick Actions</h2>
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/sites"}
|
||||
class="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 dark:bg-blue-500 dark:hover:bg-blue-600"
|
||||
>
|
||||
<.icon name="hero-building-office" class="h-5 w-5" />
|
||||
<span>Manage Sites</span>
|
||||
</.link>
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/equipment"}
|
||||
class="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 dark:bg-blue-500 dark:hover:bg-blue-600"
|
||||
>
|
||||
<.icon name="hero-server" class="h-5 w-5" />
|
||||
<span>Manage Equipment</span>
|
||||
</.link>
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/alerts"}
|
||||
class="text-sm font-medium text-blue-600 hover:text-blue-700 dark:text-blue-500 dark:hover:text-blue-400"
|
||||
class="inline-flex items-center gap-2 rounded-lg border border-zinc-300 bg-white px-4 py-2.5 text-sm font-semibold text-zinc-700 shadow-sm hover:bg-zinc-50 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-200 dark:hover:bg-zinc-700"
|
||||
>
|
||||
View All Alerts →
|
||||
<.icon name="hero-bell" class="h-5 w-5" />
|
||||
<span>View Alerts</span>
|
||||
</.link>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
<%= for alert <- Enum.take(@active_alerts, 5) do %>
|
||||
<div class="flex items-start gap-3 rounded-lg border border-red-200 bg-red-50 p-4 dark:border-red-900/50 dark:bg-red-950/30">
|
||||
<.icon
|
||||
name="hero-exclamation-triangle"
|
||||
class="h-5 w-5 flex-shrink-0 text-red-600 dark:text-red-500"
|
||||
/>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
<.link
|
||||
navigate={
|
||||
~p"/orgs/#{@current_organization.slug}/equipment/#{alert.equipment.id}"
|
||||
}
|
||||
class="hover:text-blue-600 hover:underline dark:hover:text-blue-400"
|
||||
>
|
||||
{alert.equipment.name}
|
||||
</.link>
|
||||
</h3>
|
||||
<p class="mt-0.5 text-sm text-zinc-700 dark:text-zinc-300">{alert.message}</p>
|
||||
</div>
|
||||
<span class="text-xs text-zinc-500 dark:text-zinc-400 whitespace-nowrap">
|
||||
{Calendar.strftime(alert.triggered_at, "%H:%M")}
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if length(@active_alerts) > 5 do %>
|
||||
<div class="text-center text-sm text-zinc-500 dark:text-zinc-400">
|
||||
+ {length(@active_alerts) - 5} more alerts
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="mt-8">
|
||||
<h2 class="text-xl font-semibold mb-4 text-zinc-900 dark:text-zinc-100">Quick Actions</h2>
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/sites"}
|
||||
class="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 dark:bg-blue-500 dark:hover:bg-blue-600"
|
||||
>
|
||||
<.icon name="hero-building-office" class="h-5 w-5" />
|
||||
<span>Manage Sites</span>
|
||||
</.link>
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/equipment"}
|
||||
class="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 dark:bg-blue-500 dark:hover:bg-blue-600"
|
||||
>
|
||||
<.icon name="hero-server" class="h-5 w-5" />
|
||||
<span>Manage Equipment</span>
|
||||
</.link>
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/alerts"}
|
||||
class="inline-flex items-center gap-2 rounded-lg border border-zinc-300 bg-white px-4 py-2.5 text-sm font-semibold text-zinc-700 shadow-sm hover:bg-zinc-50 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-200 dark:hover:bg-zinc-700"
|
||||
>
|
||||
<.icon name="hero-bell" class="h-5 w-5" />
|
||||
<span>View Alerts</span>
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
||||
</Layouts.authenticated>
|
||||
|
|
|
|||
|
|
@ -12,12 +12,20 @@ defmodule ToweropsWeb.EquipmentLive.Form do
|
|||
organization = socket.assigns.current_organization
|
||||
sites = Sites.list_organization_sites(organization.id)
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(:organization, organization)
|
||||
|> assign(:available_sites, sites)
|
||||
|> assign(:preselected_site_id, params["site_id"])
|
||||
|> assign(:snmp_test_result, nil)}
|
||||
# Redirect to sites page if no sites exist
|
||||
if Enum.empty?(sites) do
|
||||
{:ok,
|
||||
socket
|
||||
|> put_flash(:info, "Please create a site before adding equipment.")
|
||||
|> push_navigate(to: ~p"/orgs/#{organization.slug}/sites/new")}
|
||||
else
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(:organization, organization)
|
||||
|> assign(:available_sites, sites)
|
||||
|> assign(:preselected_site_id, params["site_id"])
|
||||
|> assign(:snmp_test_result, nil)}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
|
|||
|
|
@ -3,16 +3,19 @@ defmodule ToweropsWeb.EquipmentLive.Index do
|
|||
use ToweropsWeb, :live_view
|
||||
|
||||
alias Towerops.Equipment
|
||||
alias Towerops.Sites
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
organization = socket.assigns.current_organization
|
||||
equipment = Equipment.list_organization_equipment(organization.id)
|
||||
sites = Sites.list_organization_sites(organization.id)
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(:page_title, "Equipment")
|
||||
|> assign(:equipment, equipment)}
|
||||
|> assign(:equipment, equipment)
|
||||
|> assign(:has_sites, length(sites) > 0)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
|
|||
|
|
@ -7,72 +7,100 @@
|
|||
{@page_title}
|
||||
<:subtitle>Monitor and manage all your network equipment</:subtitle>
|
||||
<:actions>
|
||||
<.button navigate={~p"/orgs/#{@current_organization.slug}/equipment/new"} variant="primary">
|
||||
<.button
|
||||
:if={@has_sites}
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/equipment/new"}
|
||||
variant="primary"
|
||||
>
|
||||
<.icon name="hero-plus" class="h-5 w-5" /> New Equipment
|
||||
</.button>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<%= if @equipment == [] do %>
|
||||
<%= if !@has_sites do %>
|
||||
<div class="text-center py-16">
|
||||
<.icon name="hero-server" class="mx-auto h-12 w-12 text-zinc-400 dark:text-zinc-600" />
|
||||
<h3 class="mt-4 text-lg font-semibold text-zinc-900 dark:text-zinc-100">No equipment</h3>
|
||||
<.icon
|
||||
name="hero-building-office"
|
||||
class="mx-auto h-12 w-12 text-zinc-400 dark:text-zinc-600"
|
||||
/>
|
||||
<h3 class="mt-4 text-lg font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
Create a site first
|
||||
</h3>
|
||||
<p class="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Get started by adding your first equipment.
|
||||
Before adding equipment, you need to create at least one site location.
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Sites help you organize your equipment by physical location.
|
||||
</p>
|
||||
<div class="mt-6">
|
||||
<.button
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/equipment/new"}
|
||||
variant="primary"
|
||||
>
|
||||
<.icon name="hero-plus" class="h-5 w-5" /> New Equipment
|
||||
<.button navigate={~p"/orgs/#{@current_organization.slug}/sites/new"} variant="primary">
|
||||
<.icon name="hero-plus" class="h-5 w-5" /> Create Your First Site
|
||||
</.button>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<.table id="equipment" rows={@equipment}>
|
||||
<:col :let={eq} label="Name">
|
||||
<span class="font-medium">{eq.name}</span>
|
||||
</:col>
|
||||
<:col :let={eq} label="IP Address">
|
||||
<span class="font-mono text-sm">{eq.ip_address}</span>
|
||||
</:col>
|
||||
<:col :let={eq} label="Site">
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/sites/#{eq.site.id}"}
|
||||
class="text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
>
|
||||
{eq.site.name}
|
||||
</.link>
|
||||
</:col>
|
||||
<:col :let={eq} label="Status">
|
||||
<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>
|
||||
</:col>
|
||||
<:col :let={eq} label="Last Checked">
|
||||
<span class="text-sm text-zinc-600 dark:text-zinc-400">
|
||||
<%= if eq.last_checked_at do %>
|
||||
{Calendar.strftime(eq.last_checked_at, "%Y-%m-%d %H:%M")}
|
||||
<% else %>
|
||||
Never
|
||||
<% end %>
|
||||
</span>
|
||||
</:col>
|
||||
<:action :let={eq}>
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/equipment/#{eq.id}"}
|
||||
class="text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
>
|
||||
View
|
||||
</.link>
|
||||
</:action>
|
||||
</.table>
|
||||
<%= if @equipment == [] do %>
|
||||
<div class="text-center py-16">
|
||||
<.icon name="hero-server" class="mx-auto h-12 w-12 text-zinc-400 dark:text-zinc-600" />
|
||||
<h3 class="mt-4 text-lg font-semibold text-zinc-900 dark:text-zinc-100">No equipment</h3>
|
||||
<p class="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Get started by adding your first equipment.
|
||||
</p>
|
||||
<div class="mt-6">
|
||||
<.button
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/equipment/new"}
|
||||
variant="primary"
|
||||
>
|
||||
<.icon name="hero-plus" class="h-5 w-5" /> New Equipment
|
||||
</.button>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<.table id="equipment" rows={@equipment}>
|
||||
<:col :let={eq} label="Name">
|
||||
<span class="font-medium">{eq.name}</span>
|
||||
</:col>
|
||||
<:col :let={eq} label="IP Address">
|
||||
<span class="font-mono text-sm">{eq.ip_address}</span>
|
||||
</:col>
|
||||
<:col :let={eq} label="Site">
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/sites/#{eq.site.id}"}
|
||||
class="text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
>
|
||||
{eq.site.name}
|
||||
</.link>
|
||||
</:col>
|
||||
<:col :let={eq} label="Status">
|
||||
<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>
|
||||
</:col>
|
||||
<:col :let={eq} label="Last Checked">
|
||||
<span class="text-sm text-zinc-600 dark:text-zinc-400">
|
||||
<%= if eq.last_checked_at do %>
|
||||
{Calendar.strftime(eq.last_checked_at, "%Y-%m-%d %H:%M")}
|
||||
<% else %>
|
||||
Never
|
||||
<% end %>
|
||||
</span>
|
||||
</:col>
|
||||
<:action :let={eq}>
|
||||
<.link
|
||||
navigate={~p"/orgs/#{@current_organization.slug}/equipment/#{eq.id}"}
|
||||
class="text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
>
|
||||
View
|
||||
</.link>
|
||||
</:action>
|
||||
</.table>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</Layouts.authenticated>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue