- Handle empty manufacturer_id selection (select placeholder) - Log and flash when model fetch fails or returns empty - Add require Logger to reconciliation live view
173 lines
4.9 KiB
Elixir
173 lines
4.9 KiB
Elixir
defmodule ToweropsWeb.Org.GaiiaReconciliationLive do
|
|
@moduledoc false
|
|
use ToweropsWeb, :live_view
|
|
|
|
alias Towerops.Gaiia
|
|
alias Towerops.Gaiia.InventoryCreator
|
|
alias Towerops.Gaiia.Reconciliation
|
|
|
|
require Logger
|
|
|
|
@impl true
|
|
def mount(_params, _session, socket) do
|
|
org = socket.assigns.current_scope.organization
|
|
|
|
{:ok,
|
|
socket
|
|
|> assign(:organization, org)
|
|
|> assign(:page_title, t("Gaiia Inventory Reconciliation"))
|
|
|> assign(:open_create_for, nil)
|
|
|> assign(:manufacturers, [])
|
|
|> assign(:models, %{})
|
|
|> assign(:selected_manufacturer, nil)}
|
|
end
|
|
|
|
@impl true
|
|
def handle_params(params, _url, socket) do
|
|
tab = Map.get(params, "tab", "summary")
|
|
|
|
{:noreply,
|
|
socket
|
|
|> assign(:active_tab, tab)
|
|
|> load_reconciliation()}
|
|
end
|
|
|
|
@impl true
|
|
def handle_event("auto_match", _params, socket) do
|
|
org_id = socket.assigns.organization.id
|
|
|
|
{:noreply,
|
|
socket
|
|
|> put_flash(:info, match_flash(Gaiia.auto_match_untracked(org_id)))
|
|
|> load_reconciliation()}
|
|
end
|
|
|
|
def handle_event("open_create_form", %{"device_id" => device_id}, socket) do
|
|
org_id = socket.assigns.organization.id
|
|
|
|
socket =
|
|
if socket.assigns.manufacturers == [] do
|
|
case InventoryCreator.list_manufacturers(org_id) do
|
|
{:ok, manufacturers} -> assign(socket, :manufacturers, manufacturers)
|
|
{:error, _} -> socket
|
|
end
|
|
else
|
|
socket
|
|
end
|
|
|
|
{:noreply,
|
|
socket
|
|
|> assign(:open_create_for, device_id)
|
|
|> assign(:selected_manufacturer, nil)}
|
|
end
|
|
|
|
def handle_event("cancel_create", _params, socket) do
|
|
{:noreply,
|
|
socket
|
|
|> assign(:open_create_for, nil)
|
|
|> assign(:selected_manufacturer, nil)}
|
|
end
|
|
|
|
def handle_event("select_manufacturer", %{"manufacturer_id" => ""}, socket) do
|
|
{:noreply, assign(socket, :selected_manufacturer, nil)}
|
|
end
|
|
|
|
def handle_event("select_manufacturer", %{"manufacturer_id" => manufacturer_id}, socket) do
|
|
org_id = socket.assigns.organization.id
|
|
existing = socket.assigns.models
|
|
|
|
socket =
|
|
if Map.has_key?(existing, manufacturer_id) do
|
|
assign(socket, :selected_manufacturer, manufacturer_id)
|
|
else
|
|
case InventoryCreator.list_models(org_id, manufacturer_id) do
|
|
{:ok, []} ->
|
|
Logger.warning("Gaiia: no models for manufacturer #{manufacturer_id}")
|
|
|
|
socket
|
|
|> assign(:selected_manufacturer, manufacturer_id)
|
|
|> put_flash(:warning, t("No models found for this manufacturer."))
|
|
|
|
{:ok, models} ->
|
|
socket
|
|
|> assign(:models, Map.put(existing, manufacturer_id, models))
|
|
|> assign(:selected_manufacturer, manufacturer_id)
|
|
|
|
{:error, reason} ->
|
|
Logger.error("Gaiia: failed to fetch models: #{inspect(reason)}")
|
|
|
|
put_flash(socket, :error, t("Failed to load models. Check server logs."))
|
|
end
|
|
end
|
|
|
|
{:noreply, socket}
|
|
end
|
|
|
|
def handle_event("create_gaiia_item", %{"device_id" => device_id, "model_id" => model_id}, socket) do
|
|
org_id = socket.assigns.organization.id
|
|
|
|
result =
|
|
with {:ok, device} <- get_device(device_id),
|
|
{:ok, _job_id} <- InventoryCreator.create_inventory_item(org_id, device, model_id) do
|
|
{:ok, device.name}
|
|
end
|
|
|
|
{:noreply,
|
|
socket
|
|
|> assign(:open_create_for, nil)
|
|
|> assign(:selected_manufacturer, nil)
|
|
|> put_flash(:info, create_flash(result))
|
|
|> load_reconciliation()}
|
|
end
|
|
|
|
def handle_event("select_tab", %{"tab" => tab}, socket) do
|
|
org = socket.assigns.organization
|
|
|
|
{:noreply,
|
|
push_patch(socket,
|
|
to: ~p"/orgs/#{org.slug}/settings/integrations/gaiia/reconciliation?tab=#{tab}"
|
|
)}
|
|
end
|
|
|
|
defp match_flash({:ok, 0, 0, _remaining}) do
|
|
t("No matches found. Link devices manually on the mapping page.")
|
|
end
|
|
|
|
defp match_flash({:ok, ip_matched, site_matched, remaining}) do
|
|
total = ip_matched + site_matched
|
|
|
|
ngettext(
|
|
"Auto-matched %{total} device (%{ip} by IP, %{site} by site). %{remaining} remaining.",
|
|
"Auto-matched %{total} devices (%{ip} by IP, %{site} by site). %{remaining} remaining.",
|
|
total,
|
|
total: total,
|
|
ip: ip_matched,
|
|
site: site_matched,
|
|
remaining: remaining
|
|
)
|
|
end
|
|
|
|
defp get_device(device_id) do
|
|
case Towerops.Devices.get_device(device_id) do
|
|
nil -> {:error, :not_found}
|
|
device -> {:ok, device}
|
|
end
|
|
end
|
|
|
|
defp create_flash({:ok, device_name}) do
|
|
t("Created Gaiia inventory item for %{device}.", device: device_name)
|
|
end
|
|
|
|
defp create_flash({:error, :no_gaiia_integration}) do
|
|
t("Gaiia integration not configured.")
|
|
end
|
|
|
|
defp create_flash({:error, reason}) do
|
|
t("Failed to create Gaiia inventory item: %{reason}", reason: inspect(reason))
|
|
end
|
|
|
|
defp load_reconciliation(socket) do
|
|
report = Reconciliation.reconcile(socket.assigns.organization.id)
|
|
assign(socket, :report, report)
|
|
end
|
|
end
|