From 0853b6f528ea7caee176574ab2d77e0ad01f76d1 Mon Sep 17 00:00:00 2001 From: jamey Date: Tue, 3 Mar 2026 15:19:17 +0000 Subject: [PATCH] share provider connection logic between setup wizard and providers form 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 --- lib/berrypod/products.ex | 53 +++++++++++++++++++ lib/berrypod_web/live/admin/products.ex | 9 +++- lib/berrypod_web/live/admin/providers/form.ex | 39 ++------------ lib/berrypod_web/live/setup/onboarding.ex | 29 +++++----- .../berrypod_web/live/admin/products_test.exs | 2 +- .../live/admin/providers_test.exs | 10 ++++ 6 files changed, 92 insertions(+), 50 deletions(-) diff --git a/lib/berrypod/products.ex b/lib/berrypod/products.ex index d6b5e54..b8fea26 100644 --- a/lib/berrypod/products.ex +++ b/lib/berrypod/products.ex @@ -64,6 +64,59 @@ defmodule Berrypod.Products do |> Repo.insert() end + @doc """ + Tests an API key, creates the connection with config (shop_id etc), + and enqueues a product sync. Used by both setup wizard and providers form. + + Returns `{:ok, connection}` or `{:error, reason}`. + """ + def connect_provider(api_key, provider_type) do + alias Berrypod.Providers + + encrypted = + case Berrypod.Vault.encrypt(api_key) do + {:ok, enc} -> enc + _ -> nil + end + + temp_conn = %ProviderConnection{ + provider_type: provider_type, + api_key_encrypted: encrypted + } + + with {:ok, test_result} <- Providers.test_connection(temp_conn) do + name = provider_display_name(provider_type, test_result) + config = provider_config(provider_type, test_result) + + params = + %{"api_key" => api_key, "provider_type" => provider_type, "name" => name} + |> then(fn p -> if config != %{}, do: Map.put(p, "config", config), else: p end) + + case create_provider_connection(params) do + {:ok, connection} -> + enqueue_sync(connection) + {:ok, connection} + + {:error, _} = error -> + error + end + end + end + + defp provider_display_name("printify", %{shop_name: name}) when is_binary(name), do: name + defp provider_display_name("printful", %{store_name: name}) when is_binary(name), do: name + + defp provider_display_name(type, _) do + case Berrypod.Providers.Provider.get(type) do + nil -> type + info -> info.name + end + end + + defp provider_config("printify", %{shop_id: id}), do: %{"shop_id" => to_string(id)} + defp provider_config("printful", %{store_id: id}), do: %{"store_id" => to_string(id)} + defp provider_config(_, _), do: %{} + @doc """ Updates a provider connection. """ diff --git a/lib/berrypod_web/live/admin/products.ex b/lib/berrypod_web/live/admin/products.ex index 53ab2bb..05e28a2 100644 --- a/lib/berrypod_web/live/admin/products.ex +++ b/lib/berrypod_web/live/admin/products.ex @@ -184,12 +184,19 @@ defmodule BerrypodWeb.Admin.Products do
<.icon name="hero-cube" class="admin-empty-state-icon" />

No products yet

-

+

<.link navigate={~p"/admin/providers"} class="admin-link"> Connect a provider to sync your products.

+

+ Head to the + <.link navigate={~p"/admin/providers"} class="admin-link"> + providers page + + to sync your products. +

""" end diff --git a/lib/berrypod_web/live/admin/providers/form.ex b/lib/berrypod_web/live/admin/providers/form.ex index 19f5535..8ba2ddd 100644 --- a/lib/berrypod_web/live/admin/providers/form.ex +++ b/lib/berrypod_web/live/admin/providers/form.ex @@ -82,23 +82,18 @@ defmodule BerrypodWeb.Admin.Providers.Form do end defp save_connection(socket, :new, params) do + api_key = params["api_key"] || socket.assigns[:pending_api_key] provider_type = socket.assigns.provider_type - params = - params - |> Map.put("provider_type", provider_type) - |> maybe_add_config(provider_type, socket.assigns.test_result) - |> maybe_add_name(provider_type, socket.assigns.test_result) - - case Products.create_provider_connection(params) do + 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, %Ecto.Changeset{} = changeset} -> - {:noreply, assign(socket, form: to_form(changeset))} + {:error, _reason} -> + {:noreply, put_flash(socket, :error, "Could not connect — check your API key")} end end @@ -115,32 +110,6 @@ defmodule BerrypodWeb.Admin.Providers.Form do end end - # Printify returns shop_id, Printful returns store_id - defp maybe_add_config(params, "printify", {:ok, %{shop_id: shop_id}}) do - config = Map.get(params, "config", %{}) |> Map.put("shop_id", to_string(shop_id)) - Map.put(params, "config", config) - end - - defp maybe_add_config(params, "printful", {:ok, %{store_id: store_id}}) do - config = Map.get(params, "config", %{}) |> Map.put("store_id", to_string(store_id)) - Map.put(params, "config", config) - end - - defp maybe_add_config(params, _type, _result), do: params - - defp maybe_add_name(params, "printify", {:ok, %{shop_name: name}}) when is_binary(name) do - Map.put_new(params, "name", name) - end - - defp maybe_add_name(params, "printful", {:ok, %{store_name: name}}) when is_binary(name) do - Map.put_new(params, "name", name) - end - - defp maybe_add_name(params, type, _result) do - provider = Provider.get(type) - Map.put_new(params, "name", (provider && provider.name) || type) - end - defp encrypt_api_key(api_key) do case Berrypod.Vault.encrypt(api_key) do {:ok, encrypted} -> encrypted diff --git a/lib/berrypod_web/live/setup/onboarding.ex b/lib/berrypod_web/live/setup/onboarding.ex index 08836c0..2309f87 100644 --- a/lib/berrypod_web/live/setup/onboarding.ex +++ b/lib/berrypod_web/live/setup/onboarding.ex @@ -127,17 +127,8 @@ defmodule BerrypodWeb.Setup.Onboarding do else socket = assign(socket, provider_connecting: true) - name = - case Provider.get(type) do - nil -> type - info -> info.name - end - - params = %{"api_key" => api_key, "provider_type" => type, "name" => name} - - case Products.create_provider_connection(params) do + case Products.connect_provider(api_key, type) do {:ok, connection} -> - Products.enqueue_sync(connection) setup = Setup.setup_status() if setup.setup_complete do @@ -154,11 +145,17 @@ defmodule BerrypodWeb.Setup.Onboarding do |> put_flash(:info, "Connected! Product sync started in the background.")} end - {:error, _changeset} -> + {:error, :no_api_key} -> {:noreply, socket |> assign(:provider_connecting, false) - |> put_flash(:error, "Failed to save connection")} + |> put_flash(:error, "Please enter your API token")} + + {:error, _reason} -> + {:noreply, + socket + |> assign(:provider_connecting, false) + |> put_flash(:error, "Could not connect — check your API key and try again")} end end end @@ -448,7 +445,13 @@ defmodule BerrypodWeb.Setup.Onboarding do # ── Helpers ── defp account_summary(%{current_scope: %{user: user}}) when not is_nil(user) do - user.email + site_name = Settings.site_name() + + if site_name != "Store Name" do + "#{site_name} · #{user.email}" + else + user.email + end end defp account_summary(_), do: "Account created" diff --git a/test/berrypod_web/live/admin/products_test.exs b/test/berrypod_web/live/admin/products_test.exs index 48c5554..997f5cc 100644 --- a/test/berrypod_web/live/admin/products_test.exs +++ b/test/berrypod_web/live/admin/products_test.exs @@ -125,7 +125,7 @@ defmodule BerrypodWeb.Admin.ProductsTest do {:ok, _view, html} = live(conn, ~p"/admin/products") assert html =~ "No products yet" - assert html =~ "Connect a provider" + assert html =~ "providers page" end end diff --git a/test/berrypod_web/live/admin/providers_test.exs b/test/berrypod_web/live/admin/providers_test.exs index b18631a..cd77d46 100644 --- a/test/berrypod_web/live/admin/providers_test.exs +++ b/test/berrypod_web/live/admin/providers_test.exs @@ -136,6 +136,12 @@ defmodule BerrypodWeb.Admin.ProvidersTest do describe "form - new" do setup %{conn: conn, user: user} do + Application.put_env(:berrypod, :provider_modules, %{ + "printify" => MockProvider + }) + + on_exit(fn -> Application.delete_env(:berrypod, :provider_modules) end) + %{conn: log_in_user(conn, user)} end @@ -170,6 +176,10 @@ defmodule BerrypodWeb.Admin.ProvidersTest do end test "saves new connection", %{conn: conn} do + expect(MockProvider, :test_connection, fn _conn -> + {:ok, %{shop_name: "My Printify Shop", shop_id: 12345}} + end) + {:ok, view, _html} = live(conn, ~p"/admin/providers/new") {:ok, _view, html} =