add admin email settings page with provider selection
All checks were successful
deploy / deploy (push) Successful in 56s

Card radio component for picking email providers (SMTP, SendGrid, Mailjet, etc.)
with instant client-side switching via JS hook. Adapter configs are pre-rendered
and toggled without a server round-trip. Secrets are preserved when re-saving
with blank password fields. Includes from address field, test email sending,
and disconnect flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-21 19:29:34 +00:00
parent a2e46664c6
commit 366a1e6a48
17 changed files with 1176 additions and 39 deletions

View File

@@ -101,7 +101,7 @@ defmodule BerrypodWeb.Setup.Onboarding do
# ── Events: Provider ──
def handle_event("select_provider", %{"type" => type}, socket) do
def handle_event("select_provider", %{"provider_select" => %{"type" => type}}, socket) do
{:noreply,
socket
|> assign(:selected_provider, type)
@@ -363,30 +363,20 @@ defmodule BerrypodWeb.Setup.Onboarding do
attr :connecting, :boolean, required: true
defp provider_section(assigns) do
assigns = assign(assigns, :provider_options, provider_card_options(assigns.providers))
~H"""
<div>
<p class="setup-hint">Choose a print-on-demand provider and connect your API key.</p>
<div class="setup-provider-grid">
<button
:for={provider <- @providers}
type="button"
phx-click={provider.status == :available && "select_provider"}
phx-value-type={provider.type}
disabled={provider.status == :coming_soon}
class={[
"setup-provider-card",
@selected == provider.type && "setup-provider-card-selected",
provider.status == :coming_soon && "setup-provider-card-disabled"
]}
>
<span class="setup-provider-name">{provider.name}</span>
<span class="setup-provider-tagline">{provider.tagline}</span>
<span :if={provider.status == :coming_soon} class="setup-provider-badge">
Coming soon
</span>
</button>
</div>
<.form for={%{}} as={:provider_select} phx-change="select_provider">
<.card_radio_group
name="provider_select[type]"
value={@selected}
legend="Print provider"
options={@provider_options}
/>
</.form>
<%!-- API key form for selected provider --%>
<div :if={@selected} class="setup-provider-form">
@@ -548,6 +538,22 @@ defmodule BerrypodWeb.Setup.Onboarding do
end
end
defp provider_card_options(providers) do
Enum.map(providers, fn provider ->
option = %{
value: provider.type,
name: provider.name,
description: provider.tagline
}
if provider.status == :coming_soon do
Map.merge(option, %{badge: "Coming soon", disabled: true})
else
option
end
end)
end
defp format_error(:unauthorized), do: "That token doesn't seem to be valid"
defp format_error(:timeout), do: "Couldn't reach the provider — try again"
defp format_error(:provider_not_implemented), do: "This provider isn't supported yet"