2026-02-18 21:23:15 +00:00
|
|
|
defmodule BerrypodWeb.Admin.Providers.Form do
|
|
|
|
|
use BerrypodWeb, :live_view
|
2026-01-31 22:08:34 +00:00
|
|
|
|
2026-02-18 21:23:15 +00:00
|
|
|
alias Berrypod.Products
|
|
|
|
|
alias Berrypod.Products.ProviderConnection
|
|
|
|
|
alias Berrypod.Providers
|
add setup onboarding page, dashboard launch checklist, provider registry
- new /setup page with three-section onboarding (account, provider, payments)
- dashboard launch checklist with progress bar, go-live, dismiss
- provider registry on Provider module (single source of truth for metadata)
- payments registry for Stripe
- setup context made provider-agnostic (provider_connected, theme_customised, etc.)
- admin provider pages now fully registry-driven (no hardcoded provider names)
- auth flow: fresh installs redirect to /setup, signed_in_path respects setup state
- removed old /admin/setup wizard
- 840 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:34:06 +00:00
|
|
|
alias Berrypod.Providers.Provider
|
2026-01-31 22:08:34 +00:00
|
|
|
|
add setup onboarding page, dashboard launch checklist, provider registry
- new /setup page with three-section onboarding (account, provider, payments)
- dashboard launch checklist with progress bar, go-live, dismiss
- provider registry on Provider module (single source of truth for metadata)
- payments registry for Stripe
- setup context made provider-agnostic (provider_connected, theme_customised, etc.)
- admin provider pages now fully registry-driven (no hardcoded provider names)
- auth flow: fresh installs redirect to /setup, signed_in_path respects setup state
- removed old /admin/setup wizard
- 840 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:34:06 +00:00
|
|
|
@supported_types Enum.map(Provider.available(), & &1.type)
|
2026-02-15 10:53:15 +00:00
|
|
|
|
2026-01-31 22:08:34 +00:00
|
|
|
@impl true
|
|
|
|
|
def mount(params, _session, socket) do
|
|
|
|
|
{:ok, apply_action(socket, socket.assigns.live_action, params)}
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-15 10:53:15 +00:00
|
|
|
defp apply_action(socket, :new, params) do
|
|
|
|
|
provider_type = validated_type(params["type"])
|
add setup onboarding page, dashboard launch checklist, provider registry
- new /setup page with three-section onboarding (account, provider, payments)
- dashboard launch checklist with progress bar, go-live, dismiss
- provider registry on Provider module (single source of truth for metadata)
- payments registry for Stripe
- setup context made provider-agnostic (provider_connected, theme_customised, etc.)
- admin provider pages now fully registry-driven (no hardcoded provider names)
- auth flow: fresh installs redirect to /setup, signed_in_path respects setup state
- removed old /admin/setup wizard
- 840 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:34:06 +00:00
|
|
|
provider = Provider.get(provider_type)
|
2026-02-15 10:53:15 +00:00
|
|
|
|
2026-01-31 22:08:34 +00:00
|
|
|
socket
|
add setup onboarding page, dashboard launch checklist, provider registry
- new /setup page with three-section onboarding (account, provider, payments)
- dashboard launch checklist with progress bar, go-live, dismiss
- provider registry on Provider module (single source of truth for metadata)
- payments registry for Stripe
- setup context made provider-agnostic (provider_connected, theme_customised, etc.)
- admin provider pages now fully registry-driven (no hardcoded provider names)
- auth flow: fresh installs redirect to /setup, signed_in_path respects setup state
- removed old /admin/setup wizard
- 840 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:34:06 +00:00
|
|
|
|> assign(:page_title, "Connect to #{provider.name}")
|
2026-02-15 10:53:15 +00:00
|
|
|
|> assign(:provider_type, provider_type)
|
add setup onboarding page, dashboard launch checklist, provider registry
- new /setup page with three-section onboarding (account, provider, payments)
- dashboard launch checklist with progress bar, go-live, dismiss
- provider registry on Provider module (single source of truth for metadata)
- payments registry for Stripe
- setup context made provider-agnostic (provider_connected, theme_customised, etc.)
- admin provider pages now fully registry-driven (no hardcoded provider names)
- auth flow: fresh installs redirect to /setup, signed_in_path respects setup state
- removed old /admin/setup wizard
- 840 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:34:06 +00:00
|
|
|
|> assign(:provider, provider)
|
2026-02-15 10:53:15 +00:00
|
|
|
|> assign(:connection, %ProviderConnection{provider_type: provider_type})
|
2026-01-31 22:08:34 +00:00
|
|
|
|> 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)
|
add setup onboarding page, dashboard launch checklist, provider registry
- new /setup page with three-section onboarding (account, provider, payments)
- dashboard launch checklist with progress bar, go-live, dismiss
- provider registry on Provider module (single source of truth for metadata)
- payments registry for Stripe
- setup context made provider-agnostic (provider_connected, theme_customised, etc.)
- admin provider pages now fully registry-driven (no hardcoded provider names)
- auth flow: fresh installs redirect to /setup, signed_in_path respects setup state
- removed old /admin/setup wizard
- 840 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:34:06 +00:00
|
|
|
provider = Provider.get(connection.provider_type)
|
2026-01-31 22:08:34 +00:00
|
|
|
|
|
|
|
|
socket
|
add setup onboarding page, dashboard launch checklist, provider registry
- new /setup page with three-section onboarding (account, provider, payments)
- dashboard launch checklist with progress bar, go-live, dismiss
- provider registry on Provider module (single source of truth for metadata)
- payments registry for Stripe
- setup context made provider-agnostic (provider_connected, theme_customised, etc.)
- admin provider pages now fully registry-driven (no hardcoded provider names)
- auth flow: fresh installs redirect to /setup, signed_in_path respects setup state
- removed old /admin/setup wizard
- 840 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:34:06 +00:00
|
|
|
|> assign(:page_title, "#{provider.name} settings")
|
2026-02-15 10:53:15 +00:00
|
|
|
|> assign(:provider_type, connection.provider_type)
|
add setup onboarding page, dashboard launch checklist, provider registry
- new /setup page with three-section onboarding (account, provider, payments)
- dashboard launch checklist with progress bar, go-live, dismiss
- provider registry on Provider module (single source of truth for metadata)
- payments registry for Stripe
- setup context made provider-agnostic (provider_connected, theme_customised, etc.)
- admin provider pages now fully registry-driven (no hardcoded provider names)
- auth flow: fresh installs redirect to /setup, signed_in_path respects setup state
- removed old /admin/setup wizard
- 840 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:34:06 +00:00
|
|
|
|> assign(:provider, provider)
|
2026-01-31 22:08:34 +00:00
|
|
|
|> 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{
|
2026-02-15 10:53:15 +00:00
|
|
|
provider_type: socket.assigns.provider_type,
|
2026-01-31 22:08:34 +00:00
|
|
|
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
|
2026-02-15 10:53:15 +00:00
|
|
|
provider_type = socket.assigns.provider_type
|
|
|
|
|
|
2026-01-31 22:08:34 +00:00
|
|
|
params =
|
|
|
|
|
params
|
2026-02-15 10:53:15 +00:00
|
|
|
|> Map.put("provider_type", provider_type)
|
|
|
|
|
|> maybe_add_config(provider_type, socket.assigns.test_result)
|
|
|
|
|
|> maybe_add_name(provider_type, socket.assigns.test_result)
|
2026-01-31 22:08:34 +00:00
|
|
|
|
|
|
|
|
case Products.create_provider_connection(params) do
|
|
|
|
|
{:ok, _connection} ->
|
|
|
|
|
{:noreply,
|
|
|
|
|
socket
|
add setup onboarding page, dashboard launch checklist, provider registry
- new /setup page with three-section onboarding (account, provider, payments)
- dashboard launch checklist with progress bar, go-live, dismiss
- provider registry on Provider module (single source of truth for metadata)
- payments registry for Stripe
- setup context made provider-agnostic (provider_connected, theme_customised, etc.)
- admin provider pages now fully registry-driven (no hardcoded provider names)
- auth flow: fresh installs redirect to /setup, signed_in_path respects setup state
- removed old /admin/setup wizard
- 840 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:34:06 +00:00
|
|
|
|> put_flash(:info, "Connected to #{socket.assigns.provider.name}!")
|
2026-02-12 09:04:51 +00:00
|
|
|
|> push_navigate(to: ~p"/admin/settings")}
|
2026-01-31 22:08:34 +00:00
|
|
|
|
|
|
|
|
{:error, %Ecto.Changeset{} = changeset} ->
|
|
|
|
|
{:noreply, assign(socket, form: to_form(changeset))}
|
|
|
|
|
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")
|
2026-02-12 09:04:51 +00:00
|
|
|
|> push_navigate(to: ~p"/admin/settings")}
|
2026-01-31 22:08:34 +00:00
|
|
|
|
|
|
|
|
{:error, %Ecto.Changeset{} = changeset} ->
|
|
|
|
|
{:noreply, assign(socket, form: to_form(changeset))}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-15 10:53:15 +00:00
|
|
|
# Printify returns shop_id, Printful returns store_id
|
|
|
|
|
defp maybe_add_config(params, "printify", {:ok, %{shop_id: shop_id}}) do
|
2026-01-31 22:08:34 +00:00
|
|
|
config = Map.get(params, "config", %{}) |> Map.put("shop_id", to_string(shop_id))
|
|
|
|
|
Map.put(params, "config", config)
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-15 10:53:15 +00:00
|
|
|
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
|
2026-01-31 22:08:34 +00:00
|
|
|
|
2026-02-15 10:53:15 +00:00
|
|
|
defp maybe_add_name(params, "printify", {:ok, %{shop_name: name}}) when is_binary(name) do
|
|
|
|
|
Map.put_new(params, "name", name)
|
2026-01-31 22:08:34 +00:00
|
|
|
end
|
|
|
|
|
|
2026-02-15 10:53:15 +00:00
|
|
|
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
|
add setup onboarding page, dashboard launch checklist, provider registry
- new /setup page with three-section onboarding (account, provider, payments)
- dashboard launch checklist with progress bar, go-live, dismiss
- provider registry on Provider module (single source of truth for metadata)
- payments registry for Stripe
- setup context made provider-agnostic (provider_connected, theme_customised, etc.)
- admin provider pages now fully registry-driven (no hardcoded provider names)
- auth flow: fresh installs redirect to /setup, signed_in_path respects setup state
- removed old /admin/setup wizard
- 840 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:34:06 +00:00
|
|
|
provider = Provider.get(type)
|
2026-02-20 18:39:41 +00:00
|
|
|
Map.put_new(params, "name", (provider && provider.name) || type)
|
2026-01-31 22:08:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
defp encrypt_api_key(api_key) do
|
2026-02-18 21:23:15 +00:00
|
|
|
case Berrypod.Vault.encrypt(api_key) do
|
2026-01-31 22:08:34 +00:00
|
|
|
{:ok, encrypted} -> encrypted
|
|
|
|
|
_ -> nil
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-15 10:53:15 +00:00
|
|
|
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"
|
2026-01-31 22:08:34 +00:00
|
|
|
defp format_error(:unauthorized), do: "That key doesn't seem to be valid"
|
2026-02-15 10:53:15 +00:00
|
|
|
defp format_error(:timeout), do: "Couldn't reach the provider - try again"
|
2026-01-31 22:08:34 +00:00
|
|
|
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
|