defmodule BerrypodWeb.Admin.Providers.Form do use BerrypodWeb, :live_view alias Berrypod.{KeyValidation, Products} alias Berrypod.Products.ProviderConnection alias Berrypod.Providers.Provider @supported_types Enum.map(Provider.available(), & &1.type) @impl true def mount(params, _session, socket) do {:ok, apply_action(socket, socket.assigns.live_action, params)} end defp apply_action(socket, :new, params) do provider_type = validated_type(params["type"]) provider = Provider.get(provider_type) socket |> assign(:page_title, "Connect to #{provider.name}") |> assign(:provider_type, provider_type) |> assign(:provider, provider) |> assign(:connection, %ProviderConnection{provider_type: provider_type}) |> assign(:form, to_form(ProviderConnection.changeset(%ProviderConnection{}, %{}))) |> assign(:pending_api_key, nil) end defp apply_action(socket, :edit, %{"id" => id}) do connection = Products.get_provider_connection!(id) provider = Provider.get(connection.provider_type) socket |> assign(:page_title, "#{provider.name} settings") |> assign(:provider_type, connection.provider_type) |> assign(:provider, provider) |> assign(:connection, connection) |> assign(:form, to_form(ProviderConnection.changeset(connection, %{}))) |> assign(:pending_api_key, nil) end @impl true def handle_event("validate", %{"provider_connection" => params}, socket) do api_key = params["api_key"] || "" provider_type = socket.assigns.provider_type # Build base changeset changeset = socket.assigns.connection |> ProviderConnection.changeset(params) |> Map.put(:action, :validate) # Add key format validation error if key is present form = if api_key != "" do case KeyValidation.validate_provider_key(api_key, provider_type) do {:ok, _} -> to_form(changeset) {:error, message} -> changeset |> Ecto.Changeset.add_error(:api_key, message) |> to_form() end else to_form(changeset) end # Store api_key separately since changeset encrypts it immediately {:noreply, assign(socket, form: form, pending_api_key: api_key)} end @impl true def handle_event("save", %{"provider_connection" => params}, socket) do save_connection(socket, socket.assigns.live_action, params) end defp save_connection(socket, :new, params) do api_key = params["api_key"] || socket.assigns[:pending_api_key] provider_type = socket.assigns.provider_type case KeyValidation.validate_provider_key(api_key, provider_type) do {:error, message} -> form = form_with_api_key_error(socket, api_key, message) {:noreply, assign(socket, :form, form)} {:ok, api_key} -> case Products.connect_provider(api_key, provider_type) do {:ok, _connection} -> {:noreply, socket |> put_flash(:info, "Connected to #{socket.assigns.provider.name}!") |> push_navigate(to: ~p"/admin/settings")} {:error, _reason} -> form = form_with_api_key_error( socket, api_key, "Could not connect. Check your API key and try again" ) {:noreply, assign(socket, :form, form)} end end end defp save_connection(socket, :edit, params) do case Products.update_provider_connection(socket.assigns.connection, params) do {:ok, _connection} -> {:noreply, socket |> put_flash(:info, "Settings saved") |> push_navigate(to: ~p"/admin/settings")} {:error, %Ecto.Changeset{} = changeset} -> {:noreply, assign(socket, form: to_form(changeset))} end end defp form_with_api_key_error(socket, api_key, message) do socket.assigns.connection |> ProviderConnection.changeset(%{"api_key" => api_key}) |> Ecto.Changeset.add_error(:api_key, message) |> Map.put(:action, :validate) |> to_form() end defp validated_type(type) when type in @supported_types, do: type defp validated_type(_), do: "printify" end