Bring autoconfigure notification a step forwards in custom events cre… (#5912)

* Bring autoconfigure notification a step forwards in custom events creation flow

- Rather than showing a notification that custom events have been detected at the bottom of the form, we now show a modal prior to the form, that allows the user to add them instantly or set them up manually.

* Added tests for autoconfigure modal

* Clean it up a little

---------

Co-authored-by: Adam Rutkowski <hq@mtod.org>
This commit is contained in:
Sanne de Vries 2025-12-03 17:24:43 +01:00 committed by GitHub
parent 12d818af8a
commit 98e0f7276b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 179 additions and 78 deletions

View File

@ -30,13 +30,24 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
assigns[:goal_type] || "custom_events"
end
event_name_options_count = length(assigns.event_name_options)
show_autoconfigure_modal? =
case form_type do
"custom_events" when event_name_options_count > 0 and is_nil(assigns.goal) ->
true
_ ->
Map.get(socket.assigns, :show_autoconfigure_modal?, false)
end
socket =
socket
|> assign(
id: assigns.id,
context_unique_id: assigns.context_unique_id,
form: form,
event_name_options_count: length(assigns.event_name_options),
event_name_options_count: event_name_options_count,
event_name_options: Enum.map(assigns.event_name_options, &{&1, &1}),
current_user: assigns.current_user,
site_team: assigns.site_team,
@ -48,7 +59,8 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
on_save_goal: assigns.on_save_goal,
on_autoconfigure: assigns.on_autoconfigure,
goal: assigns.goal,
goal_type: assigns[:goal_type]
goal_type: assigns[:goal_type],
show_autoconfigure_modal?: show_autoconfigure_modal?
)
{:ok, socket}
@ -61,7 +73,8 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
~H"""
<div id={@id}>
{if @goal, do: edit_form(assigns)}
{if is_nil(@goal), do: create_form(assigns)}
{if is_nil(@goal) && @show_autoconfigure_modal?, do: autoconfigure_modal(assigns)}
{if is_nil(@goal) && not @show_autoconfigure_modal?, do: create_form(assigns)}
</div>
"""
end
@ -105,6 +118,41 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
"""
end
def autoconfigure_modal(assigns) do
~H"""
<div data-test-id="autoconfigure-modal">
<.title>
We detected {@event_name_options_count} custom {if @event_name_options_count == 1,
do: "event",
else: "events"}.
</.title>
<p class="mt-2 py-2 text-sm text-gray-600 dark:text-gray-400 text-pretty">
These events have been sent from your site in the past 6 months but aren't yet configured as goals. Add them instantly or set one up manually.
</p>
<div class="flex justify-end gap-3">
<.button
theme="secondary"
phx-click="add-manually"
phx-target={@myself}
>
Add manually
</.button>
<.button
phx-click="autoconfigure"
phx-target={@myself}
>
<Heroicons.plus class="size-4" />
Add {@event_name_options_count} {if @event_name_options_count == 1,
do: "event",
else: "events"}
</.button>
</div>
</div>
"""
end
def create_form(assigns) do
~H"""
<.form :let={f} for={@form} phx-submit="save-goal" phx-target={@myself}>
@ -139,20 +187,6 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
<.button type="submit" class="w-full">
Add goal
</.button>
<button
:if={@form_type == "custom_events" && @event_name_options_count > 0}
class="mt-4 text-sm hover:underline text-indigo-600 dark:text-indigo-400 text-left"
phx-click="autoconfigure"
phx-target={@myself}
>
<span :if={@event_name_options_count > 1}>
Already sending custom events? We've found {@event_name_options_count} custom events from the last 6 months that are not yet configured as goals. Click here to add them.
</span>
<span :if={@event_name_options_count == 1}>
Already sending custom events? We've found 1 custom event from the last 6 months that is not yet configured as a goal. Click here to add it.
</span>
</button>
</.form>
"""
end
@ -166,13 +200,14 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
def pageview_fields(assigns) do
~H"""
<div id="pageviews-form" class="py-2" {@rest}>
<div class="text-sm pb-6 text-gray-500 dark:text-gray-400 text-justify rounded-md">
Pageview goals allow you to measure how many people visit a specific page or section of your site. Learn more in <.styled_link
<div class="text-sm pb-6 text-gray-600 dark:text-gray-400 text-pretty">
Pageview goals allow you to measure how many people visit a specific page or section of your site.
<.styled_link
href="https://plausible.io/docs/pageview-goals"
new_tab={true}
>
our docs
</.styled_link>.
Learn more
</.styled_link>
</div>
<.label for={"page_path_input_#{@suffix}"}>
@ -245,12 +280,13 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
~H"""
<div id="scroll-form" class="py-2" x-data={@js} {@rest}>
<div class="text-sm pb-6 text-gray-500 dark:text-gray-400 text-justify rounded-md">
Scroll Depth goals allow you to see how many people scroll beyond your desired scroll depth percentage threshold. Learn more in <.styled_link
Scroll Depth goals allow you to see how many people scroll beyond your desired scroll depth percentage threshold.
<.styled_link
href="https://plausible.io/docs/scroll-depth"
new_tab={true}
>
our docs
</.styled_link>.
Learn more
</.styled_link>
</div>
<.label for={"scroll_threshold_input_#{@suffix}"}>
@ -460,9 +496,14 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
end
def handle_event("autoconfigure", _params, socket) do
socket = assign(socket, show_autoconfigure_modal?: false)
{:noreply, socket.assigns.on_autoconfigure.(socket)}
end
def handle_event("add-manually", _params, socket) do
{:noreply, assign(socket, show_autoconfigure_modal?: false)}
end
def suggest_page_paths(input, site) do
query =
Plausible.Stats.Query.parse_and_build!(
@ -523,7 +564,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
disabled={not @has_access_to_revenue_goals?}
/>
<span class={[
"ml-3 text-sm font-medium",
"ml-3 text-sm/6 font-medium",
if(@has_access_to_revenue_goals?,
do: "text-gray-900 dark:text-gray-100",
else: "text-gray-500 dark:text-gray-400"

View File

@ -125,7 +125,7 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
end
test "creates a custom event", %{conn: conn, site: site} do
lv = get_liveview(conn, site)
lv = get_liveview(conn, site) |> open_modal_with_goal_type("custom_events")
refute render(lv) =~ "SampleCustomEvent"
lv = open_modal_with_goal_type(lv, "custom_events")
@ -148,7 +148,7 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
new_site(team: team)
site = new_consolidated_view(team)
lv = get_liveview(conn, site)
lv = get_liveview(conn, site) |> open_modal_with_goal_type("custom_events")
assert render(lv) =~ "Add goal for consolidated view"
refute element_exists?(render(lv), @revenue_goal_settings)
@ -165,7 +165,7 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
@tag :ee_only
test "creates a revenue goal", %{conn: conn, site: site} do
lv = get_liveview(conn, site)
lv = get_liveview(conn, site) |> open_modal_with_goal_type("custom_events")
refute render(lv) =~ "SampleRevenueGoal"
assert element_exists?(render(lv), @revenue_goal_settings)
@ -188,7 +188,7 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
end
test "creates a pageview goal", %{conn: conn, site: site} do
lv = get_liveview(conn, site)
lv = get_liveview(conn, site) |> open_modal_with_goal_type("pageviews")
refute render(lv) =~ "Visit /page/**"
lv
@ -205,7 +205,7 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
test "fails to create a goal above limit", %{conn: conn, site: site} do
for i <- 1..10, do: {:ok, _} = Plausible.Goals.create(site, %{"event_name" => "G#{i}"})
lv = get_liveview(conn, site)
lv = get_liveview(conn, site) |> open_modal_with_goal_type("pageviews")
refute render(lv) =~ "Visit /page/**"
lv
@ -423,6 +423,10 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
lv = get_liveview(conn, site) |> open_modal_with_goal_type("custom_events")
lv
|> element("button[phx-click='add-manually']")
|> render_click()
type_into_combo(lv, "event_name_input_modalseq0", "One")
html = render(lv)
@ -446,7 +450,11 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
# Delete the goal
goal = Plausible.Repo.get_by(Plausible.Goal, site_id: site.id, event_name: "EventOne")
html = lv |> element(~s/button#delete-goal-#{goal.id}/) |> render_click()
lv |> element(~s/button#delete-goal-#{goal.id}/) |> render_click()
lv |> element("button[phx-click='add-manually']") |> render_click()
html = render(lv)
assert text_of_element(html, "#goals-form-modalseq0") =~ "EventOne"
refute text_of_element(html, "#goals-form-modalseq0") =~ "EventTwo"
@ -454,6 +462,105 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
end
end
describe "Autoconfigure goals from custom events modal" do
setup [:create_user, :log_in, :create_site]
test "shows autoconfigure modal when opening custom events modal with available events", %{
conn: conn,
site: site
} do
populate_stats(site, [
build(:pageview, pathname: "/go/home"),
build(:event, name: "Signup"),
build(:event, name: "Newsletter Signup"),
build(:event, name: "Purchase")
])
lv = get_liveview(conn, site) |> open_modal_with_goal_type("custom_events")
html = render(lv)
assert html =~ "We detected 3 custom"
assert html =~ "These events have been sent from your site in the past 6 months"
end
test "clicking 'Add manually' shows the regular form", %{conn: conn, site: site} do
populate_stats(site, [
build(:pageview, pathname: "/go/home"),
build(:event, name: "Signup"),
build(:event, name: "Newsletter Signup"),
build(:event, name: "Purchase")
])
lv = get_liveview(conn, site) |> open_modal_with_goal_type("custom_events")
html = render(lv)
assert html =~ "We detected 3 custom"
lv
|> element("button[phx-click='add-manually']")
|> render_click()
html = render(lv)
refute html =~ "We detected"
assert html =~ "Add goal for"
end
test "autoconfigure button adds all events", %{conn: conn, site: site} do
populate_stats(site, [
build(:pageview, pathname: "/go/home"),
build(:event, name: "Signup"),
build(:event, name: "Newsletter Signup"),
build(:event, name: "Purchase")
])
lv = get_liveview(conn, site) |> open_modal_with_goal_type("custom_events")
lv
|> element("button[phx-click='autoconfigure']")
|> render_click()
# Render again to process the async :autoconfigure message
_html = render(lv)
goals = Plausible.Goals.for_site(site)
assert length(goals) == 3
assert Enum.any?(goals, &(&1.event_name == "Signup"))
assert Enum.any?(goals, &(&1.event_name == "Newsletter Signup"))
assert Enum.any?(goals, &(&1.event_name == "Purchase"))
end
test "autoconfigure modal does not show when all events are already goals", %{
conn: conn,
site: site
} do
populate_stats(site, [
build(:pageview, pathname: "/go/home"),
build(:event, name: "Signup"),
build(:event, name: "Newsletter Signup"),
build(:event, name: "Purchase")
])
lv = get_liveview(conn, site)
html = render(lv)
assert element_exists?(html, "[data-test-id='autoconfigure-modal']")
lv
|> element("button[phx-click='autoconfigure']")
|> render_click()
html = render(lv)
refute element_exists?(html, "[data-test-id='autoconfigure-modal']")
lv = open_modal_with_goal_type(lv, "custom_events")
html = render(lv)
refute element_exists?(html, "[data-test-id='autoconfigure-modal']")
refute html =~ "We detected"
refute html =~ "from the last 6 months"
end
end
defp type_into_combo(lv, id, text) do
lv
|> element("input##{id}")

View File

@ -246,53 +246,6 @@ defmodule PlausibleWeb.Live.GoalSettingsTest do
refute html =~ "No goals found for this site. Please refine or"
end
test "auto-configuring custom event goals", %{conn: conn, site: site} do
populate_stats(site, [
build(:event, name: "Signup"),
build(:event, name: "Newsletter Signup"),
build(:event, name: "Purchase")
])
autoconfigure_button_selector = ~s/button[phx-click="autoconfigure"]/
assert_suggested_event_name_count = fn html, number ->
assert text_of_element(html, autoconfigure_button_selector) =~
"found #{number} custom events from the last 6 months that are not yet configured as goals"
end
{lv, html} = get_liveview(conn, site, with_html?: true)
# At first, 3 event names are suggested
assert_suggested_event_name_count.(html, 3)
# Add one goal
lv
|> element("#goals-form-modal form")
|> render_submit(%{goal: %{event_name: "Signup"}})
html = render(lv)
# Now two goals are suggested because one is already added
assert_suggested_event_name_count.(html, 2)
# Delete the goal
goal = Plausible.Repo.get_by(Plausible.Goal, site_id: site.id, event_name: "Signup")
html = lv |> element(~s/button#delete-goal-#{goal.id}/) |> render_click()
# Suggested event name count should be 3 again
assert_suggested_event_name_count.(html, 3)
# Autoconfigure all custom event goals
lv
|> element(autoconfigure_button_selector)
|> render_click()
html = render(lv)
# All possible goals exist - no suggestions anymore
refute html =~ "from the last 6 months"
end
end
on_ee do