analytics/lib/plausible_web/live/goal_settings.ex

128 lines
3.5 KiB
Elixir
Raw Normal View History

Improve goal settings UX (#3293) * Add Heroicons dependency * Add name_of/1 html helper Currently with Floki there's no way to query for `[name=foo[some]]` selector * Update changelog * Make goal deletion possible with only goal id * Remove stale goal controllers * Improve ComboBox component - make sure the list options are always of the parent input width - allow passing a suggestion function instead of a module * Stale fixup * Update routes * Use the new goals route in funnel settings * Use a function in the funnel combo * Use function in the props combo * Remove old goals form * Implement new goal settings * Update moduledoc * Fix revenue switch in dark mode * Connect live socket on goal settings page * Fixup * Use Heroicons.trash icon * Tweak goals search input * Remove unused alias * Fix search/button alignment * Fix backspace icon alignment * Delegate :superadmin check to get_for_user/3 I'll do props settings separately, it's work in progress in a branch on top of this one already. cc @ukutaht * Rename socket assigns * Fixup to 5c9f58e * Fixup * Render ComboBox suggestions asynchronously This commit: - prevents redundant work by checking the socket connection - allows passing no options to the ComboBox component, so that when combined with the `async` option, the options are asynchronously initialized post-render - allows updating the suggestions asynchronously with the `async` option set to `true` - helpful in case of DB queries used for suggestions * Update tests * Throttle comboboxes * Update tests * Dim the search input * Use debounce=200 in ComboBox component * Move creatable option to the top * Ensure there's always a leading slash for goals * Test pageview goals with leading / missing * Make the modal scrollable on small viewports
2023-09-04 14:44:22 +03:00
defmodule PlausibleWeb.Live.GoalSettings do
@moduledoc """
LiveView allowing listing, creating and deleting goals.
"""
use Phoenix.LiveView
use Phoenix.HTML
use Plausible.Funnel
alias Plausible.{Sites, Goals}
def mount(
_params,
Props Settings UI to match Goals Settings (#3322) * Add hint to creatable ComboBoxes without suggestions available * Load external resources once in funnel settings * Load external resources once in goal settings * Make Custom Props Settings UI match Goal Settings * Remove unnecessary goals query This should be done only once in the live view * Remove funnels feature flag * fixup * Make the modal scrollable * By default, focus first suggestion for creatables * Add sample props to seeds * Load all suggestions asynchronously, unless `Mix.env == :test` * ComboBox: Fix inconsistent suggestions We require "Create ..." element to be only focused when there are no suggestions available. This causes some issues, depending on the state, the least focusable index might be either 0 ("Create...") or 1. This patch addresses all the quirks with focus. * Fix ComboBox max results message So that AlpineJS doesn't think it's a focusable option. * Keep the state up to date when changing props * Update seeds with sensible prop names * Make escape work for closing combobox suggestions Co-authored-by: Uku Taht <Uku.taht@gmail.com> * Revert "Make escape work for closing combobox suggestions" This reverts commit 306866d2a15d652eaa0327f1b96a302de5e426e5. @ukutaht unfortunately this makes it impossible to select an suggestion. * Revert "Revert "Make escape work for closing combobox suggestions"" This reverts commit 4844857812d21ea1233fe3b9ae6f95364c945a14. * Make ESC great again * Improve readability --------- Co-authored-by: Uku Taht <Uku.taht@gmail.com>
2023-09-13 15:55:29 +03:00
%{"site_id" => site_id, "domain" => domain, "current_user_id" => user_id},
Improve goal settings UX (#3293) * Add Heroicons dependency * Add name_of/1 html helper Currently with Floki there's no way to query for `[name=foo[some]]` selector * Update changelog * Make goal deletion possible with only goal id * Remove stale goal controllers * Improve ComboBox component - make sure the list options are always of the parent input width - allow passing a suggestion function instead of a module * Stale fixup * Update routes * Use the new goals route in funnel settings * Use a function in the funnel combo * Use function in the props combo * Remove old goals form * Implement new goal settings * Update moduledoc * Fix revenue switch in dark mode * Connect live socket on goal settings page * Fixup * Use Heroicons.trash icon * Tweak goals search input * Remove unused alias * Fix search/button alignment * Fix backspace icon alignment * Delegate :superadmin check to get_for_user/3 I'll do props settings separately, it's work in progress in a branch on top of this one already. cc @ukutaht * Rename socket assigns * Fixup to 5c9f58e * Fixup * Render ComboBox suggestions asynchronously This commit: - prevents redundant work by checking the socket connection - allows passing no options to the ComboBox component, so that when combined with the `async` option, the options are asynchronously initialized post-render - allows updating the suggestions asynchronously with the `async` option set to `true` - helpful in case of DB queries used for suggestions * Update tests * Throttle comboboxes * Update tests * Dim the search input * Use debounce=200 in ComboBox component * Move creatable option to the top * Ensure there's always a leading slash for goals * Test pageview goals with leading / missing * Make the modal scrollable on small viewports
2023-09-04 14:44:22 +03:00
socket
) do
Props Settings UI to match Goals Settings (#3322) * Add hint to creatable ComboBoxes without suggestions available * Load external resources once in funnel settings * Load external resources once in goal settings * Make Custom Props Settings UI match Goal Settings * Remove unnecessary goals query This should be done only once in the live view * Remove funnels feature flag * fixup * Make the modal scrollable * By default, focus first suggestion for creatables * Add sample props to seeds * Load all suggestions asynchronously, unless `Mix.env == :test` * ComboBox: Fix inconsistent suggestions We require "Create ..." element to be only focused when there are no suggestions available. This causes some issues, depending on the state, the least focusable index might be either 0 ("Create...") or 1. This patch addresses all the quirks with focus. * Fix ComboBox max results message So that AlpineJS doesn't think it's a focusable option. * Keep the state up to date when changing props * Update seeds with sensible prop names * Make escape work for closing combobox suggestions Co-authored-by: Uku Taht <Uku.taht@gmail.com> * Revert "Make escape work for closing combobox suggestions" This reverts commit 306866d2a15d652eaa0327f1b96a302de5e426e5. @ukutaht unfortunately this makes it impossible to select an suggestion. * Revert "Revert "Make escape work for closing combobox suggestions"" This reverts commit 4844857812d21ea1233fe3b9ae6f95364c945a14. * Make ESC great again * Improve readability --------- Co-authored-by: Uku Taht <Uku.taht@gmail.com>
2023-09-13 15:55:29 +03:00
socket =
socket
|> assign_new(:site, fn ->
Sites.get_for_user!(user_id, domain, [:owner, :admin, :super_admin])
end)
|> assign_new(:all_goals, fn %{site: site} ->
Goals.for_site(site, preload_funnels?: true)
end)
Improve goal settings UX (#3293) * Add Heroicons dependency * Add name_of/1 html helper Currently with Floki there's no way to query for `[name=foo[some]]` selector * Update changelog * Make goal deletion possible with only goal id * Remove stale goal controllers * Improve ComboBox component - make sure the list options are always of the parent input width - allow passing a suggestion function instead of a module * Stale fixup * Update routes * Use the new goals route in funnel settings * Use a function in the funnel combo * Use function in the props combo * Remove old goals form * Implement new goal settings * Update moduledoc * Fix revenue switch in dark mode * Connect live socket on goal settings page * Fixup * Use Heroicons.trash icon * Tweak goals search input * Remove unused alias * Fix search/button alignment * Fix backspace icon alignment * Delegate :superadmin check to get_for_user/3 I'll do props settings separately, it's work in progress in a branch on top of this one already. cc @ukutaht * Rename socket assigns * Fixup to 5c9f58e * Fixup * Render ComboBox suggestions asynchronously This commit: - prevents redundant work by checking the socket connection - allows passing no options to the ComboBox component, so that when combined with the `async` option, the options are asynchronously initialized post-render - allows updating the suggestions asynchronously with the `async` option set to `true` - helpful in case of DB queries used for suggestions * Update tests * Throttle comboboxes * Update tests * Dim the search input * Use debounce=200 in ComboBox component * Move creatable option to the top * Ensure there's always a leading slash for goals * Test pageview goals with leading / missing * Make the modal scrollable on small viewports
2023-09-04 14:44:22 +03:00
{:ok,
assign(socket,
Props Settings UI to match Goals Settings (#3322) * Add hint to creatable ComboBoxes without suggestions available * Load external resources once in funnel settings * Load external resources once in goal settings * Make Custom Props Settings UI match Goal Settings * Remove unnecessary goals query This should be done only once in the live view * Remove funnels feature flag * fixup * Make the modal scrollable * By default, focus first suggestion for creatables * Add sample props to seeds * Load all suggestions asynchronously, unless `Mix.env == :test` * ComboBox: Fix inconsistent suggestions We require "Create ..." element to be only focused when there are no suggestions available. This causes some issues, depending on the state, the least focusable index might be either 0 ("Create...") or 1. This patch addresses all the quirks with focus. * Fix ComboBox max results message So that AlpineJS doesn't think it's a focusable option. * Keep the state up to date when changing props * Update seeds with sensible prop names * Make escape work for closing combobox suggestions Co-authored-by: Uku Taht <Uku.taht@gmail.com> * Revert "Make escape work for closing combobox suggestions" This reverts commit 306866d2a15d652eaa0327f1b96a302de5e426e5. @ukutaht unfortunately this makes it impossible to select an suggestion. * Revert "Revert "Make escape work for closing combobox suggestions"" This reverts commit 4844857812d21ea1233fe3b9ae6f95364c945a14. * Make ESC great again * Improve readability --------- Co-authored-by: Uku Taht <Uku.taht@gmail.com>
2023-09-13 15:55:29 +03:00
site_id: site_id,
domain: domain,
displayed_goals: socket.assigns.all_goals,
Improve goal settings UX (#3293) * Add Heroicons dependency * Add name_of/1 html helper Currently with Floki there's no way to query for `[name=foo[some]]` selector * Update changelog * Make goal deletion possible with only goal id * Remove stale goal controllers * Improve ComboBox component - make sure the list options are always of the parent input width - allow passing a suggestion function instead of a module * Stale fixup * Update routes * Use the new goals route in funnel settings * Use a function in the funnel combo * Use function in the props combo * Remove old goals form * Implement new goal settings * Update moduledoc * Fix revenue switch in dark mode * Connect live socket on goal settings page * Fixup * Use Heroicons.trash icon * Tweak goals search input * Remove unused alias * Fix search/button alignment * Fix backspace icon alignment * Delegate :superadmin check to get_for_user/3 I'll do props settings separately, it's work in progress in a branch on top of this one already. cc @ukutaht * Rename socket assigns * Fixup to 5c9f58e * Fixup * Render ComboBox suggestions asynchronously This commit: - prevents redundant work by checking the socket connection - allows passing no options to the ComboBox component, so that when combined with the `async` option, the options are asynchronously initialized post-render - allows updating the suggestions asynchronously with the `async` option set to `true` - helpful in case of DB queries used for suggestions * Update tests * Throttle comboboxes * Update tests * Dim the search input * Use debounce=200 in ComboBox component * Move creatable option to the top * Ensure there's always a leading slash for goals * Test pageview goals with leading / missing * Make the modal scrollable on small viewports
2023-09-04 14:44:22 +03:00
add_goal?: false,
current_user_id: user_id,
filter_text: ""
)}
end
# Flash sharing with live views within dead views can be done via re-rendering the flash partial.
# Normally, we'd have to use live_patch which we can't do with views unmounted at the router it seems.
def render(assigns) do
~H"""
<div id="goal-settings-main">
<.live_component id="embedded_liveview_flash" module={PlausibleWeb.Live.Flash} flash={@flash} />
<%= if @add_goal? do %>
<%= live_render(
@socket,
PlausibleWeb.Live.GoalSettings.Form,
id: "goals-form",
session: %{
"current_user_id" => @current_user_id,
"domain" => @domain,
"site_id" => @site_id,
"rendered_by" => self()
}
) %>
<% end %>
<.live_component
module={PlausibleWeb.Live.GoalSettings.List}
id="goals-list"
goals={@displayed_goals}
domain={@domain}
filter_text={@filter_text}
/>
</div>
"""
end
def handle_event("reset-filter-text", _params, socket) do
{:noreply, assign(socket, filter_text: "", displayed_goals: socket.assigns.all_goals)}
end
def handle_event("filter", %{"filter-text" => filter_text}, socket) do
new_list =
PlausibleWeb.Live.Components.ComboBox.StaticSearch.suggest(
filter_text,
socket.assigns.all_goals
)
{:noreply, assign(socket, displayed_goals: new_list, filter_text: filter_text)}
end
def handle_event("add-goal", _value, socket) do
{:noreply, assign(socket, add_goal?: true)}
end
def handle_event("delete-goal", %{"goal-id" => goal_id}, socket) do
goal_id = String.to_integer(goal_id)
case Plausible.Goals.delete(goal_id, socket.assigns.site_id) do
:ok ->
socket =
socket
|> put_flash(:success, "Goal deleted successfully")
|> assign(
all_goals: Enum.reject(socket.assigns.all_goals, &(&1.id == goal_id)),
displayed_goals: Enum.reject(socket.assigns.displayed_goals, &(&1.id == goal_id))
)
Process.send_after(self(), :clear_flash, 5000)
{:noreply, socket}
_ ->
{:noreply, socket}
end
end
def handle_info(:cancel_add_goal, socket) do
{:noreply, assign(socket, add_goal?: false)}
end
def handle_info({:goal_added, goal}, socket) do
socket =
socket
|> assign(
add_goal?: false,
filter_text: "",
all_goals: [goal | socket.assigns.all_goals],
displayed_goals: [goal | socket.assigns.all_goals]
)
|> put_flash(:success, "Goal saved successfully")
{:noreply, socket}
end
def handle_info(:clear_flash, socket) do
{:noreply, clear_flash(socket)}
end
end