defmodule ToweropsWeb.OnboardingLive do @moduledoc false use ToweropsWeb, :live_view alias Towerops.Agents alias Towerops.Integrations alias Towerops.Integrations.Integration alias Towerops.Organizations alias Towerops.Sites alias Towerops.Sites.Site @steps [:snmp, :site, :billing, :agent] @billing_providers [ %{id: "gaiia", name: "Gaiia", icon: "hero-user-group"}, %{id: "sonar", name: "Sonar", icon: "hero-currency-dollar"}, %{id: "splynx", name: "Splynx", icon: "hero-banknotes"}, %{id: "visp", name: "VISP", icon: "hero-cloud"} ] @impl true def mount(_params, _session, socket) do organization = socket.assigns.current_scope.organization changeset = Organizations.change_organization(organization) site_changeset = Sites.change_site(%Site{}, %{}) {:ok, socket |> assign(:page_title, t("Setup")) |> assign(:organization, organization) |> assign(:step, :snmp) |> assign(:steps, @steps) |> assign(:form, to_form(changeset)) |> assign(:site_form, to_form(site_changeset, as: "site")) |> assign(:billing_providers, @billing_providers) |> assign(:selected_provider, nil) |> assign(:integration_form, nil) |> assign(:agent_token, nil) |> assign(:agent_token_value, nil)} end @impl true def handle_params(_params, _url, socket) do {:noreply, socket} end # SNMP step events @impl true def handle_event("validate_snmp", %{"organization" => params}, socket) do changeset = socket.assigns.organization |> Organizations.change_organization(params) |> Map.put(:action, :validate) {:noreply, assign(socket, :form, to_form(changeset))} end @impl true def handle_event("save_snmp", %{"organization" => params}, socket) do case Organizations.update_organization(socket.assigns.organization, params) do {:ok, organization} -> {:noreply, socket |> assign(:organization, organization) |> put_flash(:info, t("SNMP configuration saved")) |> assign(:step, :site)} {:error, changeset} -> {:noreply, assign(socket, :form, to_form(changeset))} end end # Site step events @impl true def handle_event("validate_site", %{"site" => params}, socket) do changeset = %Site{} |> Sites.change_site(params) |> Map.put(:action, :validate) {:noreply, assign(socket, :site_form, to_form(changeset, as: "site"))} end @impl true def handle_event("save_site", %{"site" => params}, socket) do org = socket.assigns.organization attrs = Map.put(params, "organization_id", org.id) case Sites.create_site(attrs) do {:ok, _site} -> # Enable sites on the org if not already if !org.use_sites do Organizations.update_organization(org, %{use_sites: true}) end {:noreply, socket |> put_flash(:info, t("Site created")) |> assign(:step, :billing)} {:error, changeset} -> {:noreply, assign(socket, :site_form, to_form(changeset, as: "site"))} end end # Billing step events @impl true def handle_event("select_provider", %{"provider" => provider}, socket) do form = %Integration{} |> Integrations.change_integration(%{provider: provider}) |> to_form() {:noreply, socket |> assign(:selected_provider, provider) |> assign(:integration_form, form)} end @impl true def handle_event("validate_integration", %{"integration" => params}, socket) do changeset = %Integration{} |> Integrations.change_integration(normalize_integration_params(params, socket.assigns.selected_provider)) |> Map.put(:action, :validate) {:noreply, assign(socket, :integration_form, to_form(changeset))} end @impl true def handle_event("save_integration", %{"integration" => params}, socket) do org = socket.assigns.organization provider = socket.assigns.selected_provider attrs = params |> normalize_integration_params(provider) |> Map.put(:provider, provider) |> Map.put(:enabled, true) case Integrations.create_integration(org.id, attrs) do {:ok, _integration} -> {:noreply, socket |> put_flash(:info, t("Integration saved")) |> assign(:step, :agent) |> load_agent_token()} {:error, changeset} -> {:noreply, assign(socket, :integration_form, to_form(changeset))} end end # Navigation events @impl true def handle_event("skip", _params, socket) do next = next_step(socket.assigns.step) socket = if next == :done do complete_onboarding(socket) else socket = assign(socket, :step, next) if next == :agent, do: load_agent_token(socket), else: socket end {:noreply, socket} end @impl true def handle_event("finish", _params, socket) do {:noreply, complete_onboarding(socket)} end @impl true def handle_event("go_to_step", %{"step" => step}, socket) do step = String.to_existing_atom(step) socket = if step == :agent do socket |> assign(:step, step) |> load_agent_token() else assign(socket, :step, step) end {:noreply, socket} end # Private helpers defp next_step(:snmp), do: :site defp next_step(:site), do: :billing defp next_step(:billing), do: :agent defp next_step(:agent), do: :done defp step_index(:snmp), do: 0 defp step_index(:site), do: 1 defp step_index(:billing), do: 2 defp step_index(:agent), do: 3 defp step_label(:snmp), do: t("SNMP") defp step_label(:site), do: t("Site") defp step_label(:billing), do: t("Billing") defp step_label(:agent), do: t("Agent") defp complete_onboarding(socket) do org = socket.assigns.organization Organizations.update_organization(org, %{onboarding_completed: true}) socket |> put_flash(:info, t("Setup complete")) |> push_navigate(to: ~p"/dashboard") end defp load_agent_token(socket) do org = socket.assigns.organization tokens = Agents.list_organization_agent_tokens(org.id) case tokens do [token | _] -> assign(socket, agent_token: token, agent_token_value: nil) [] -> case Agents.create_agent_token(org.id, "Default Agent") do {:ok, token, token_value} -> assign(socket, agent_token: token, agent_token_value: token_value) {:error, _} -> assign(socket, agent_token: nil, agent_token_value: nil) end end end defp normalize_integration_params(params, "sonar") do %{ credentials: %{ "instance_url" => String.trim(Map.get(params, "instance_url", "")), "api_token" => Map.get(params, "api_token", "") } } end defp normalize_integration_params(params, "splynx") do %{ credentials: %{ "instance_url" => String.trim(Map.get(params, "instance_url", "")), "api_key" => Map.get(params, "api_key", ""), "api_secret" => Map.get(params, "api_secret", "") } } end defp normalize_integration_params(params, _provider) do %{credentials: %{"api_key" => Map.get(params, "api_key", "")}} end end