Extract Products.connect_provider/2 that tests the connection, fetches shop_id, creates the record, and enqueues sync. Both the setup wizard and the providers form now use this shared function instead of duplicating the flow. Also makes the products empty state context-aware (distinguishes "no provider" from "provider connected but no products"). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
136 lines
4.5 KiB
Elixir
136 lines
4.5 KiB
Elixir
defmodule BerrypodWeb.Admin.Providers.Form do
|
|
use BerrypodWeb, :live_view
|
|
|
|
alias Berrypod.Products
|
|
alias Berrypod.Products.ProviderConnection
|
|
alias Berrypod.Providers
|
|
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(:testing, false)
|
|
|> assign(:test_result, nil)
|
|
|> 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(:testing, false)
|
|
|> assign(:test_result, nil)
|
|
|> assign(:pending_api_key, nil)
|
|
end
|
|
|
|
@impl true
|
|
def handle_event("validate", %{"provider_connection" => params}, socket) do
|
|
form =
|
|
socket.assigns.connection
|
|
|> ProviderConnection.changeset(params)
|
|
|> Map.put(:action, :validate)
|
|
|> to_form()
|
|
|
|
# Store api_key separately since changeset encrypts it immediately
|
|
{:noreply, assign(socket, form: form, pending_api_key: params["api_key"])}
|
|
end
|
|
|
|
@impl true
|
|
def handle_event("test_connection", _params, socket) do
|
|
socket = assign(socket, testing: true, test_result: nil)
|
|
|
|
api_key =
|
|
socket.assigns[:pending_api_key] ||
|
|
ProviderConnection.get_api_key(socket.assigns.connection)
|
|
|
|
if api_key && api_key != "" do
|
|
temp_conn = %ProviderConnection{
|
|
provider_type: socket.assigns.provider_type,
|
|
api_key_encrypted: encrypt_api_key(api_key)
|
|
}
|
|
|
|
result = Providers.test_connection(temp_conn)
|
|
{:noreply, assign(socket, testing: false, test_result: result)}
|
|
else
|
|
{:noreply, assign(socket, testing: false, test_result: {:error, :no_api_key})}
|
|
end
|
|
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 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} ->
|
|
{:noreply, put_flash(socket, :error, "Could not connect — check your API key")}
|
|
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 encrypt_api_key(api_key) do
|
|
case Berrypod.Vault.encrypt(api_key) do
|
|
{:ok, encrypted} -> encrypted
|
|
_ -> nil
|
|
end
|
|
end
|
|
|
|
defp validated_type(type) when type in @supported_types, do: type
|
|
defp validated_type(_), do: "printify"
|
|
|
|
# Shared helpers used by the template
|
|
|
|
defp connection_name({:ok, %{shop_name: name}}), do: name
|
|
defp connection_name({:ok, %{store_name: name}}), do: name
|
|
defp connection_name(_), do: nil
|
|
|
|
defp format_error(:no_api_key), do: "Please enter your API key"
|
|
defp format_error(:unauthorized), do: "That key doesn't seem to be valid"
|
|
defp format_error(:timeout), do: "Couldn't reach the provider - try again"
|
|
defp format_error({:http_error, _code}), do: "Something went wrong - try again"
|
|
defp format_error(error) when is_binary(error), do: error
|
|
defp format_error(_), do: "Connection failed - check your key and try again"
|
|
end
|