add admin email settings page with provider selection
All checks were successful
deploy / deploy (push) Successful in 56s
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:
@@ -519,6 +519,99 @@ defmodule BerrypodWeb.CoreComponents do
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders a radio card group — a set of selectable cards backed by radio inputs.
|
||||
|
||||
Each option is a map with `:value` and `:name`, plus optional `:description`,
|
||||
`:tags`, `:url`, `:badge`, and `:disabled` keys.
|
||||
|
||||
The `display` attr controls card content layout:
|
||||
- `:tags` (default) — name + tag pills + short description
|
||||
- `:description` — name + description text + link
|
||||
|
||||
## Examples
|
||||
|
||||
<.card_radio_group
|
||||
name="email[adapter]"
|
||||
value={@selected}
|
||||
legend="Email provider"
|
||||
options={[
|
||||
%{value: "postmark", name: "Postmark", description: "Fast email.", tags: ["Transactional", "US"]},
|
||||
%{value: "smtp", name: "SMTP", description: "Any SMTP server.", tags: ["Any type"]}
|
||||
]}
|
||||
/>
|
||||
"""
|
||||
attr :name, :string, required: true
|
||||
attr :value, :string, default: nil
|
||||
attr :legend, :string, required: true
|
||||
attr :options, :list, required: true
|
||||
attr :disabled, :boolean, default: false
|
||||
attr :display, :atom, default: :tags, values: [:description, :tags]
|
||||
|
||||
def card_radio_group(assigns) do
|
||||
~H"""
|
||||
<fieldset class="card-radio-fieldset" disabled={@disabled}>
|
||||
<legend class="admin-label">{@legend}</legend>
|
||||
<div class="card-radio-grid">
|
||||
<label
|
||||
:for={option <- @options}
|
||||
class={[
|
||||
"card-radio-card",
|
||||
@value == option.value && "card-radio-card-selected",
|
||||
option[:disabled] && "card-radio-card-disabled"
|
||||
]}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
id={"#{@name}-#{option.value}"}
|
||||
name={@name}
|
||||
value={option.value}
|
||||
checked={@value == option.value}
|
||||
disabled={option[:disabled] || @disabled}
|
||||
class="card-radio-input"
|
||||
/>
|
||||
<span class="card-radio-name">{option.name}</span>
|
||||
<.card_radio_content option={option} display={@display} />
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
"""
|
||||
end
|
||||
|
||||
attr :option, :map, required: true
|
||||
attr :display, :atom, required: true
|
||||
|
||||
defp card_radio_content(%{display: :tags} = assigns) do
|
||||
~H"""
|
||||
<span :if={@option[:tags]} class="card-radio-tags">
|
||||
<span :for={tag <- @option.tags} class="card-radio-tag">{tag}</span>
|
||||
</span>
|
||||
<span :if={@option[:description]} class="card-radio-description">
|
||||
{@option.description}
|
||||
</span>
|
||||
<span :if={@option[:badge]} class="card-radio-badge">{@option.badge}</span>
|
||||
"""
|
||||
end
|
||||
|
||||
defp card_radio_content(assigns) do
|
||||
~H"""
|
||||
<span :if={@option[:description]} class="card-radio-description">
|
||||
{@option.description}
|
||||
</span>
|
||||
<span :if={@option[:badge]} class="card-radio-badge">{@option.badge}</span>
|
||||
<a
|
||||
:if={@option[:url]}
|
||||
href={@option.url}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="card-radio-link"
|
||||
onclick="event.stopPropagation();"
|
||||
>
|
||||
{@option.name} ↗
|
||||
</a>
|
||||
"""
|
||||
end
|
||||
|
||||
def show_modal(js \\ %JS{}, id) when is_binary(id) do
|
||||
js
|
||||
|> JS.exec("showModal()", to: "##{id}")
|
||||
|
||||
Reference in New Issue
Block a user