- Sites schema with hierarchical parent-child relationships
- CRUD operations for sites
- Sites context with helper functions
- LiveView pages:
- /orgs/:slug/sites - List all sites
- /orgs/:slug/sites/new - Create new site
- /orgs/:slug/sites/:id - View site details
- /orgs/:slug/sites/:id/edit - Edit site
- Support for site locations and descriptions
- Site tree builder for hierarchy visualization
2. Equipment Management
- Equipment schema with monitoring fields
- IP address validation (IPv4 & IPv6)
- Equipment status tracking (up/down/unknown)
- Customizable check intervals per equipment
- Equipment context with CRUD operations
- LiveView pages:
- /orgs/:slug/equipment - List all equipment
- /orgs/:slug/equipment/new - Add equipment
- /orgs/:slug/equipment/:id - View equipment details
- /orgs/:slug/equipment/:id/edit - Edit equipment
- Equipment can be added from site pages
- Status badges and last checked timestamps
3. Database Schema
- sites table with self-referencing parent_site_id
- equipment table with status tracking
- All migrations run successfully
- Proper indexes on foreign keys and status
4. Features Implemented
- ✅ IP address validation using :inet.parse_address
- ✅ Site hierarchy with parent-child relationships
- ✅ Equipment linked to sites
- ✅ Monitoring enabled/disabled per equipment
- ✅ Customizable check intervals (30s - 3600s)
- ✅ Status tracking (up/down/unknown)
- ✅ Timestamps for last check and last status change
- ✅ Organization-scoped data (users only see their org's data)
108 lines
3.3 KiB
Text
108 lines
3.3 KiB
Text
<.header>
|
|
{@page_title}
|
|
<:subtitle>{@site.location}</:subtitle>
|
|
<:actions>
|
|
<.link
|
|
navigate={~p"/orgs/#{@current_organization.slug}/sites/#{@site.id}/edit"}
|
|
class="btn btn-sm"
|
|
>
|
|
<.icon name="hero-pencil" class="w-4 h-4" /> Edit
|
|
</.link>
|
|
<.button phx-click="delete" data-confirm="Are you sure?" class="btn-error btn-sm">
|
|
<.icon name="hero-trash" class="w-4 h-4" /> Delete
|
|
</.button>
|
|
</:actions>
|
|
</.header>
|
|
|
|
<div class="mt-8 grid gap-6 md:grid-cols-2">
|
|
<div>
|
|
<h3 class="text-lg font-semibold mb-4">Site Details</h3>
|
|
<dl class="space-y-2">
|
|
<%= if @site.parent_site do %>
|
|
<div>
|
|
<dt class="text-sm font-medium text-base-content/60">Parent Site</dt>
|
|
<dd class="mt-1">
|
|
<.link
|
|
navigate={~p"/orgs/#{@current_organization.slug}/sites/#{@site.parent_site.id}"}
|
|
class="link link-primary"
|
|
>
|
|
{@site.parent_site.name}
|
|
</.link>
|
|
</dd>
|
|
</div>
|
|
<% end %>
|
|
<%= if @site.location do %>
|
|
<div>
|
|
<dt class="text-sm font-medium text-base-content/60">Location</dt>
|
|
<dd class="mt-1">{@site.location}</dd>
|
|
</div>
|
|
<% end %>
|
|
<%= if @site.description do %>
|
|
<div>
|
|
<dt class="text-sm font-medium text-base-content/60">Description</dt>
|
|
<dd class="mt-1">{@site.description}</dd>
|
|
</div>
|
|
<% end %>
|
|
</dl>
|
|
|
|
<%= if @site.child_sites != [] do %>
|
|
<div class="mt-6">
|
|
<h4 class="text-sm font-semibold mb-2">Child Sites</h4>
|
|
<ul class="space-y-1">
|
|
<li :for={child <- @site.child_sites}>
|
|
<.link
|
|
navigate={~p"/orgs/#{@current_organization.slug}/sites/#{child.id}"}
|
|
class="link link-primary text-sm"
|
|
>
|
|
{child.name}
|
|
</.link>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
|
|
<div>
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="text-lg font-semibold">Equipment</h3>
|
|
<.link
|
|
navigate={~p"/orgs/#{@current_organization.slug}/equipment/new?site_id=#{@site.id}"}
|
|
class="btn btn-sm btn-primary"
|
|
>
|
|
<.icon name="hero-plus" class="w-4 h-4" /> Add Equipment
|
|
</.link>
|
|
</div>
|
|
|
|
<%= if @equipment == [] do %>
|
|
<p class="text-sm text-base-content/60">No equipment at this site yet.</p>
|
|
<% else %>
|
|
<div class="space-y-2">
|
|
<div
|
|
:for={eq <- @equipment}
|
|
class="flex items-center justify-between p-3 bg-base-200 rounded"
|
|
>
|
|
<div>
|
|
<p class="font-medium">{eq.name}</p>
|
|
<p class="text-sm text-base-content/60">{eq.ip_address}</p>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<span class={[
|
|
"badge badge-sm",
|
|
eq.status == :up && "badge-success",
|
|
eq.status == :down && "badge-error",
|
|
eq.status == :unknown && "badge-ghost"
|
|
]}>
|
|
{eq.status |> to_string() |> String.upcase()}
|
|
</span>
|
|
<.link
|
|
navigate={~p"/orgs/#{@current_organization.slug}/equipment/#{eq.id}"}
|
|
class="btn btn-xs"
|
|
>
|
|
View
|
|
</.link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
</div>
|