add no-JS fallback for provider forms

Progressive enhancement: provider form now works without JavaScript.
Forms POST to ProvidersController (create/update), which handles
validation and redirects with flash messages.

With JS: LiveView phx-submit handles save, navigates with flash.
Without JS: Form POSTs to controller, redirects with flash.

Completes Task 3 of notification overhaul (admin forms migration).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
jamey 2026-03-08 07:30:16 +00:00
parent 3e29a89fff
commit 0834437340
4 changed files with 70 additions and 3 deletions

View File

@ -29,7 +29,7 @@ Tier 1 MVP complete. Tier 2 production readiness complete (except Litestream and
- Fully Tailwind-free CSS (12 KB gzipped shop+theme, 95 KB gzipped admin total)
- CI pipeline (compile warnings, format, credo, dialyzer, tests)
- Deployed on Fly.io with observability (LiveDashboard, ErrorTracker, structured logging)
- 1716+ tests passing, 99-100 PageSpeed mobile
- 1759+ tests passing, 99-100 PageSpeed mobile
## Next up
@ -75,7 +75,7 @@ Replace floating toast/flash messages with inline feedback and persistent top ba
|---|------|-----|--------|
| 1 | Build inline feedback component | 1.5h | done |
| 2 | Build persistent top banner component (replaces flash) | 1.5h | done |
| 3 | Migrate admin forms to inline feedback (theme, pages, settings, email, providers) | 3h | in progress |
| 3 | Migrate admin forms to inline feedback (theme, pages, settings, email, providers) | 3h | done |
| 4 | Migrate remaining admin pages (media, products, activity, newsletter, redirects, nav) | 2h | planned |
| 5 | Migrate shop pages (cart, contact, checkout, auth) | 2h | planned |
| 6 | Migrate setup wizard notifications | 1h | planned |

View File

@ -0,0 +1,54 @@
defmodule BerrypodWeb.ProvidersController do
@moduledoc """
No-JS fallback for provider connection forms.
With JS enabled, the LiveView handles everything. Without JS,
the form POSTs here and we redirect back to the settings page.
"""
use BerrypodWeb, :controller
alias Berrypod.{KeyValidation, Products}
alias Berrypod.Providers.Provider
def create(conn, %{"provider_connection" => params}) do
api_key = params["api_key"]
provider_type = params["provider_type"]
provider = Provider.get(provider_type)
case KeyValidation.validate_provider_key(api_key, provider_type) do
{:error, message} ->
conn
|> put_flash(:error, message)
|> redirect(to: ~p"/admin/providers/new?type=#{provider_type}")
{:ok, api_key} ->
case Products.connect_provider(api_key, provider_type) do
{:ok, _connection} ->
conn
|> put_flash(:info, "Connected to #{provider.name}!")
|> redirect(to: ~p"/admin/settings")
{:error, _reason} ->
conn
|> put_flash(:error, "Could not connect. Check your API key and try again")
|> redirect(to: ~p"/admin/providers/new?type=#{provider_type}")
end
end
end
def update(conn, %{"id" => id, "provider_connection" => params}) do
connection = Products.get_provider_connection!(id)
case Products.update_provider_connection(connection, params) do
{:ok, _connection} ->
conn
|> put_flash(:info, "Settings saved")
|> redirect(to: ~p"/admin/settings")
{:error, _changeset} ->
conn
|> put_flash(:error, "Could not save settings")
|> redirect(to: ~p"/admin/providers/#{id}/edit")
end
end
end

View File

@ -27,7 +27,18 @@
</div>
<% end %>
<.form for={@form} id="provider-form" phx-change="validate" phx-submit="save">
<.form
for={@form}
id="provider-form"
action={
if @live_action == :new,
do: ~p"/admin/providers",
else: ~p"/admin/providers/#{@connection.id}"
}
method="post"
phx-change="validate"
phx-submit="save"
>
<input type="hidden" name="provider_connection[provider_type]" value={@provider_type} />
<.input

View File

@ -144,6 +144,8 @@ defmodule BerrypodWeb.Router do
post "/settings/from-address", SettingsController, :update_from_address
post "/settings/stripe/signing-secret", SettingsController, :update_signing_secret
post "/navigation", NavigationController, :save
post "/providers", ProvidersController, :create
post "/providers/:id", ProvidersController, :update
live_session :admin,
layout: {BerrypodWeb.Layouts, :admin},