analytics/lib/plausible_web/live/goal_settings.ex
hq1 0822bc61df
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 306866d2a1.

@ukutaht unfortunately this makes it impossible to select
an suggestion.

* Revert "Revert "Make escape work for closing combobox suggestions""

This reverts commit 4844857812.

* Make ESC great again

* Improve readability

---------

Co-authored-by: Uku Taht <Uku.taht@gmail.com>
2023-09-13 14:55:29 +02:00

128 lines
3.5 KiB
Elixir

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,
%{"site_id" => site_id, "domain" => domain, "current_user_id" => user_id},
socket
) do
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)
{:ok,
assign(socket,
site_id: site_id,
domain: domain,
displayed_goals: socket.assigns.all_goals,
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