diff --git a/PROGRESS.md b/PROGRESS.md index 796daca..230e94a 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -69,13 +69,13 @@ Based on usability testing (March 2026). Reworks the entire setup flow into a si ### Notification system overhaul ([plan](docs/plans/notification-overhaul.md)) -Replace floating toast/flash messages with inline feedback and persistent top banners. 110+ flash messages across 28 files to migrate. +Replace floating toast/flash messages with inline feedback and persistent top banners. ~140 flash messages across 28 files to migrate. Inline feedback for form saves, banners for page-level outcomes. | # | Task | Est | Status | |---|------|-----|--------| -| 1 | Build inline feedback component | 1.5h | planned | -| 2 | Build persistent top banner component (replaces flash) | 1.5h | planned | -| 3 | Migrate admin forms to inline feedback (theme, pages, settings, email, providers) | 3h | planned | +| 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 | | 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 | diff --git a/lib/berrypod_web/live/admin/email_settings.ex b/lib/berrypod_web/live/admin/email_settings.ex index 7d084cd..229bdda 100644 --- a/lib/berrypod_web/live/admin/email_settings.ex +++ b/lib/berrypod_web/live/admin/email_settings.ex @@ -37,6 +37,7 @@ defmodule BerrypodWeb.Admin.EmailSettings do |> assign(:test_retryable, false) |> assign(:from_checklist, false) |> assign(:field_errors, flash_errors) + |> assign(:save_status, :idle) |> assign(:form, to_form(%{}, as: :email))} end @@ -93,6 +94,8 @@ defmodule BerrypodWeb.Admin.EmailSettings do socket.assigns.current_scope.user.email ) do {:ok, _adapter_info} -> + Process.send_after(self(), :clear_save_status, 3000) + {:noreply, socket |> assign(:adapter_key, adapter_key) @@ -102,7 +105,7 @@ defmodule BerrypodWeb.Admin.EmailSettings do |> assign(:field_errors, %{}) |> assign(:test_result, nil) |> assign(:test_error, nil) - |> put_flash(:info, "Settings saved — send a test email to check it works")} + |> assign(:save_status, :saved)} {:error, field_errors} when is_map(field_errors) -> {:noreply, assign(socket, :field_errors, field_errors)} @@ -142,6 +145,10 @@ defmodule BerrypodWeb.Admin.EmailSettings do # Swoosh test adapter sends {:email, ...} messages — ignore them def handle_info({:email, _}, socket), do: {:noreply, socket} + def handle_info(:clear_save_status, socket) do + {:noreply, assign(socket, :save_status, :idle)} + end + @impl true def render(assigns) do ~H""" @@ -235,6 +242,7 @@ defmodule BerrypodWeb.Admin.EmailSettings do values={@all_values[adapter.key] || %{}} field_errors={if(@adapter_key == adapter.key, do: @field_errors, else: %{})} env_locked={@env_locked} + save_status={if(@adapter_key == adapter.key, do: @save_status, else: :idle)} /> @@ -336,6 +344,7 @@ defmodule BerrypodWeb.Admin.EmailSettings do attr :values, :map, required: true attr :field_errors, :map, required: true attr :env_locked, :boolean, required: true + attr :save_status, :atom, default: :idle defp adapter_config(assigns) do ~H""" @@ -377,6 +386,7 @@ defmodule BerrypodWeb.Admin.EmailSettings do <.button phx-disable-with="Saving..."> Save settings + <.inline_feedback status={@save_status} message="Now send a test email" /> <% end %> diff --git a/lib/berrypod_web/live/admin/pages/editor.ex b/lib/berrypod_web/live/admin/pages/editor.ex index f7e8f0b..8573f1c 100644 --- a/lib/berrypod_web/live/admin/pages/editor.ex +++ b/lib/berrypod_web/live/admin/pages/editor.ex @@ -32,6 +32,7 @@ defmodule BerrypodWeb.Admin.Pages.Editor do |> assign(:history, []) |> assign(:future, []) |> assign(:dirty, false) + |> assign(:save_status, :idle) |> assign(:show_picker, false) |> assign(:picker_filter, "") |> assign(:expanded, MapSet.new()) @@ -383,13 +384,18 @@ defmodule BerrypodWeb.Admin.Pages.Editor do case Pages.save_page(slug, %{title: page_data.title, blocks: blocks}) do {:ok, _page} -> + Process.send_after(self(), :clear_save_status, 3000) + {:noreply, socket |> assign(:dirty, false) - |> put_flash(:info, "Page saved")} + |> assign(:save_status, :saved)} {:error, _changeset} -> - {:noreply, put_flash(socket, :error, "Failed to save page")} + {:noreply, + socket + |> assign(:save_status, :error) + |> put_flash(:error, "Failed to save page")} end end @@ -515,6 +521,13 @@ defmodule BerrypodWeb.Admin.Pages.Editor do end end + # ── Handle info ────────────────────────────────────────────────── + + @impl true + def handle_info(:clear_save_status, socket) do + {:noreply, assign(socket, :save_status, :idle)} + end + # ── Render ─────────────────────────────────────────────────────── @impl true @@ -586,6 +599,7 @@ defmodule BerrypodWeb.Admin.Pages.Editor do > Save + <.inline_feedback status={@save_status} /> diff --git a/lib/berrypod_web/live/admin/settings.ex b/lib/berrypod_web/live/admin/settings.ex index 0e1c6b5..9c3f4e8 100644 --- a/lib/berrypod_web/live/admin/settings.ex +++ b/lib/berrypod_web/live/admin/settings.ex @@ -16,6 +16,8 @@ defmodule BerrypodWeb.Admin.Settings do |> assign(:site_live, Settings.site_live?()) |> assign(:cart_recovery_enabled, Settings.abandoned_cart_recovery_enabled?()) |> assign(:from_address, Settings.get_setting("email_from_address") || user.email) + |> assign(:from_address_status, :idle) + |> assign(:signing_secret_status, :idle) |> assign_stripe_state() |> assign_products_state() |> assign_account_state(user)} @@ -116,11 +118,12 @@ defmodule BerrypodWeb.Admin.Settings do if address != "" do Settings.put_setting("email_from_address", address) + Process.send_after(self(), :clear_from_address_status, 3000) {:noreply, socket |> assign(:from_address, address) - |> put_flash(:info, "From address saved")} + |> assign(:from_address_status, :saved)} else {:noreply, put_flash(socket, :error, "From address can't be blank")} end @@ -181,11 +184,12 @@ defmodule BerrypodWeb.Admin.Settings do {:noreply, put_flash(socket, :error, "Please enter a signing secret")} else StripeSetup.save_signing_secret(secret) + Process.send_after(self(), :clear_signing_secret_status, 3000) socket = socket |> assign_stripe_state() - |> put_flash(:info, "Webhook signing secret saved") + |> assign(:signing_secret_status, :saved) {:noreply, socket} end @@ -289,6 +293,17 @@ defmodule BerrypodWeb.Admin.Settings do end end + # -- Clear status messages -- + + @impl true + def handle_info(:clear_from_address_status, socket) do + {:noreply, assign(socket, :from_address_status, :idle)} + end + + def handle_info(:clear_signing_secret_status, socket) do + {:noreply, assign(socket, :signing_secret_status, :idle)} + end + # -- Render -- @impl true @@ -364,6 +379,7 @@ defmodule BerrypodWeb.Admin.Settings do stripe_has_signing_secret={@stripe_has_signing_secret} secret_form={@secret_form} advanced_open={@stripe_advanced_open} + signing_secret_status={@signing_secret_status} /> <% end %> @@ -448,6 +464,7 @@ defmodule BerrypodWeb.Admin.Settings do placeholder="noreply@yourshop.com" /> <.button phx-disable-with="Saving...">Save + <.inline_feedback status={@from_address_status} /> @@ -704,6 +721,7 @@ defmodule BerrypodWeb.Admin.Settings do />
<.button phx-disable-with="Saving...">Save signing secret + <.inline_feedback status={@signing_secret_status} />
<% else %> @@ -733,6 +751,7 @@ defmodule BerrypodWeb.Admin.Settings do />
<.button phx-disable-with="Saving...">Save signing secret + <.inline_feedback status={@signing_secret_status} />
diff --git a/test/berrypod_web/live/admin/email_settings_test.exs b/test/berrypod_web/live/admin/email_settings_test.exs index 5863096..80583ef 100644 --- a/test/berrypod_web/live/admin/email_settings_test.exs +++ b/test/berrypod_web/live/admin/email_settings_test.exs @@ -97,7 +97,7 @@ defmodule BerrypodWeb.Admin.EmailSettingsTest do }) |> render_submit() - assert html =~ "Settings saved" + assert html =~ "Now send a test email" assert Settings.get_setting("email_adapter") == "brevo" end diff --git a/test/berrypod_web/live/admin/pages_test.exs b/test/berrypod_web/live/admin/pages_test.exs index 7ec881d..f2a0a59 100644 --- a/test/berrypod_web/live/admin/pages_test.exs +++ b/test/berrypod_web/live/admin/pages_test.exs @@ -208,7 +208,7 @@ defmodule BerrypodWeb.Admin.PagesTest do render_click(view, "save") - assert render(view) =~ "Page saved" + assert has_element?(view, ".admin-inline-feedback-saved") saved = Pages.get_page("home") assert length(saved.blocks) == 3 diff --git a/test/berrypod_web/live/admin/settings_test.exs b/test/berrypod_web/live/admin/settings_test.exs index bccb3f5..9719936 100644 --- a/test/berrypod_web/live/admin/settings_test.exs +++ b/test/berrypod_web/live/admin/settings_test.exs @@ -113,7 +113,7 @@ defmodule BerrypodWeb.Admin.SettingsTest do }) |> render_submit() - assert html =~ "Webhook signing secret saved" + assert has_element?(view, ".admin-inline-feedback-saved") assert html =~ "whsec_te•••456" end @@ -261,7 +261,7 @@ defmodule BerrypodWeb.Admin.SettingsTest do |> form("form[phx-submit=\"save_from_address\"]", %{from_address: "shop@example.com"}) |> render_submit() - assert html =~ "From address saved" + assert has_element?(view, ".admin-inline-feedback-saved") assert Settings.get_setting("email_from_address") == "shop@example.com" end end