add forgiving API key validation with inline errors
Add KeyValidation module for format-checking API keys before attempting connections. Auto-strips whitespace, detects common mistakes (e.g. pasting a Stripe publishable key), and returns helpful error messages. Inline field errors across all three entry points: - Setup wizard: provider + Stripe keys - Admin provider form: simplified to single Connect button - Email settings: per-field errors instead of flash toasts Also: plain text inputs for all API keys (not password fields), accessible error states (aria-invalid, role=alert, thick border, bold text), inner_block slot declaration on error component. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -205,6 +205,8 @@ defmodule BerrypodWeb.CoreComponents do
|
||||
end
|
||||
|
||||
def input(%{type: "select"} = assigns) do
|
||||
assigns = assign(assigns, :error_id, assigns.id && "#{assigns.id}-error")
|
||||
|
||||
~H"""
|
||||
<div class="admin-fieldset">
|
||||
<label>
|
||||
@@ -214,18 +216,22 @@ defmodule BerrypodWeb.CoreComponents do
|
||||
name={@name}
|
||||
class={[@class || "admin-select", @errors != [] && (@error_class || "admin-input-error")]}
|
||||
multiple={@multiple}
|
||||
aria-invalid={@errors != [] && "true"}
|
||||
aria-describedby={@errors != [] && @error_id}
|
||||
{@rest}
|
||||
>
|
||||
<option :if={@prompt} value="">{@prompt}</option>
|
||||
{Phoenix.HTML.Form.options_for_select(@options, @value)}
|
||||
</select>
|
||||
</label>
|
||||
<.error :for={msg <- @errors}>{msg}</.error>
|
||||
<.error :for={msg <- @errors} id={@error_id}>{msg}</.error>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
def input(%{type: "textarea"} = assigns) do
|
||||
assigns = assign(assigns, :error_id, assigns.id && "#{assigns.id}-error")
|
||||
|
||||
~H"""
|
||||
<div class="admin-fieldset">
|
||||
<label>
|
||||
@@ -237,16 +243,20 @@ defmodule BerrypodWeb.CoreComponents do
|
||||
@class || "admin-textarea",
|
||||
@errors != [] && (@error_class || "admin-input-error")
|
||||
]}
|
||||
aria-invalid={@errors != [] && "true"}
|
||||
aria-describedby={@errors != [] && @error_id}
|
||||
{@rest}
|
||||
>{Phoenix.HTML.Form.normalize_value("textarea", @value)}</textarea>
|
||||
</label>
|
||||
<.error :for={msg <- @errors}>{msg}</.error>
|
||||
<.error :for={msg <- @errors} id={@error_id}>{msg}</.error>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
# All other inputs text, datetime-local, url, password, etc. are handled here...
|
||||
def input(assigns) do
|
||||
assigns = assign(assigns, :error_id, assigns.id && "#{assigns.id}-error")
|
||||
|
||||
~H"""
|
||||
<div class="admin-fieldset">
|
||||
<label>
|
||||
@@ -260,19 +270,24 @@ defmodule BerrypodWeb.CoreComponents do
|
||||
@class || "admin-input",
|
||||
@errors != [] && (@error_class || "admin-input-error")
|
||||
]}
|
||||
aria-invalid={@errors != [] && "true"}
|
||||
aria-describedby={@errors != [] && @error_id}
|
||||
{@rest}
|
||||
/>
|
||||
</label>
|
||||
<.error :for={msg <- @errors}>{msg}</.error>
|
||||
<.error :for={msg <- @errors} id={@error_id}>{msg}</.error>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
# Helper used by inputs to generate form errors
|
||||
attr :id, :string, default: nil
|
||||
slot :inner_block, required: true
|
||||
|
||||
defp error(assigns) do
|
||||
~H"""
|
||||
<p class="admin-error">
|
||||
<.icon name="hero-exclamation-circle" class="size-5" />
|
||||
<p class="admin-error" id={@id} role="alert">
|
||||
<.icon name="hero-exclamation-circle" class="admin-error-icon" />
|
||||
{render_slot(@inner_block)}
|
||||
</p>
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user