towerops/lib/towerops_web/live/equipment_live/form.ex
Graham McIntire 1bd72dac78
1. Sites Management
- 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)
2025-12-21 13:39:19 -06:00

94 lines
2.9 KiB
Elixir

defmodule ToweropsWeb.EquipmentLive.Form do
@moduledoc false
use ToweropsWeb, :live_view
alias Towerops.Equipment
alias Towerops.Equipment.Equipment, as: EquipmentSchema
alias Towerops.Sites
@impl true
def mount(params, _session, socket) 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"])}
end
@impl true
def handle_params(params, _url, socket) do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end
defp apply_action(socket, :new, _params) do
equipment_attrs = %{monitoring_enabled: true, check_interval_seconds: 300}
equipment_attrs =
if socket.assigns.preselected_site_id do
Map.put(equipment_attrs, :site_id, socket.assigns.preselected_site_id)
else
equipment_attrs
end
changeset = Equipment.change_equipment(%EquipmentSchema{}, equipment_attrs)
socket
|> assign(:page_title, "New Equipment")
|> assign(:equipment, %EquipmentSchema{})
|> assign(:form, to_form(changeset))
end
defp apply_action(socket, :edit, %{"id" => id}) do
equipment = Equipment.get_equipment!(id)
changeset = Equipment.change_equipment(equipment)
socket
|> assign(:page_title, "Edit Equipment")
|> assign(:equipment, equipment)
|> assign(:form, to_form(changeset))
end
@impl true
def handle_event("validate", %{"equipment" => equipment_params}, socket) do
changeset =
socket.assigns.equipment
|> Equipment.change_equipment(equipment_params)
|> Map.put(:action, :validate)
{:noreply, assign(socket, :form, to_form(changeset))}
end
@impl true
def handle_event("save", %{"equipment" => equipment_params}, socket) do
save_equipment(socket, socket.assigns.live_action, equipment_params)
end
defp save_equipment(socket, :new, equipment_params) do
case Equipment.create_equipment(equipment_params) do
{:ok, equipment} ->
{:noreply,
socket
|> put_flash(:info, "Equipment created successfully")
|> push_navigate(to: ~p"/orgs/#{socket.assigns.organization.slug}/equipment/#{equipment.id}")}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, :form, to_form(changeset))}
end
end
defp save_equipment(socket, :edit, equipment_params) do
case Equipment.update_equipment(socket.assigns.equipment, equipment_params) do
{:ok, equipment} ->
{:noreply,
socket
|> put_flash(:info, "Equipment updated successfully")
|> push_navigate(to: ~p"/orgs/#{socket.assigns.organization.slug}/equipment/#{equipment.id}")}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, :form, to_form(changeset))}
end
end
end