defmodule PlausibleWeb.Live.GoalSettings.Form do @moduledoc """ Live view for the goal creation form """ use Phoenix.LiveComponent, global_prefixes: ~w(x-) use Plausible import PlausibleWeb.Live.Components.Form alias PlausibleWeb.Live.Components.ComboBox alias Plausible.Repo def update(assigns, socket) do site = Repo.preload(assigns.site, [:owner]) owner = Plausible.Users.with_subscription(site.owner) site = %{site | owner: owner} has_access_to_revenue_goals? = Plausible.Billing.Feature.RevenueGoals.check_availability(owner) == :ok form = %Plausible.Goal{} |> Plausible.Goal.changeset() |> to_form() 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: Enum.map(assigns.event_name_options, &{&1, &1}), current_user: assigns.current_user, domain: assigns.domain, selected_tab: "custom_events", tab_sequence_id: 0, site: site, has_access_to_revenue_goals?: has_access_to_revenue_goals?, existing_goals: assigns.existing_goals, on_save_goal: assigns.on_save_goal, on_autoconfigure: assigns.on_autoconfigure ) {:ok, socket} end def render(assigns) do ~H"""
<.form :let={f} x-data="{ tabSelectionInProgress: false }" for={@form} phx-submit="save-goal" phx-target={@myself} >

Add Goal for <%= @domain %>

<.tabs selected_tab={@selected_tab} myself={@myself} /> <.custom_event_fields :if={@selected_tab == "custom_events"} x-show="!tabSelectionInProgress" f={f} suffix={suffix(@context_unique_id, @tab_sequence_id)} current_user={@current_user} site={@site} existing_goals={@existing_goals} goal_options={@event_name_options} has_access_to_revenue_goals?={@has_access_to_revenue_goals?} x-init="tabSelectionInProgress = false" /> <.pageview_fields :if={@selected_tab == "pageviews"} x-show="!tabSelectionInProgress" f={f} suffix={suffix(@context_unique_id, @tab_sequence_id)} site={@site} x-init="tabSelectionInProgress = false" />
Add Goal →
""" end attr(:f, Phoenix.HTML.Form) attr(:site, Plausible.Site) attr(:suffix, :string) attr(:rest, :global) def pageview_fields(assigns) do ~H"""
<.label for={"page_path_input_#{@suffix}"}> Page Path <.live_component id={"page_path_input_#{@suffix}"} submit_name="goal[page_path]" class={[ "py-2" ]} module={ComboBox} suggest_fun={fn input, _options -> suggest_page_paths(input, @site) end} creatable /> <.error :for={{msg, opts} <- @f[:page_path].errors}> <%= Enum.reduce(opts, msg, fn {key, value}, acc -> String.replace(acc, "%{#{key}}", fn _ -> to_string(value) end) end) %>
""" end attr(:f, Phoenix.HTML.Form) attr(:site, Plausible.Site) attr(:current_user, Plausible.Auth.User) attr(:suffix, :string) attr(:existing_goals, :list) attr(:goal_options, :list) attr(:has_access_to_revenue_goals?, :boolean) attr(:rest, :global) def custom_event_fields(assigns) do ~H"""
Custom Events are not tracked by default - you have to configure them on your site to be sent to Plausible. See examples and learn more in our docs.
<.label for={"event_name_input_#{@suffix}"}> Event Name <.live_component id={"event_name_input_#{@suffix}"} submit_name="goal[event_name]" placeholder="e.g. Signup" class={[ "py-2" ]} module={ComboBox} suggest_fun={fn input, _options -> suggest_event_names(input, @site, @existing_goals) end} options={@goal_options} creatable />
<.live_component id={"currency_input_#{@suffix}"} submit_name={@f[:currency].name} module={ComboBox} suggest_fun={ on_ee do fn "", [] -> Plausible.Goal.Revenue.currency_options() input, options -> ComboBox.StaticSearch.suggest(input, options, weight_threshold: 0.8) end end } />
<.error :for={{msg, opts} <- @f[:event_name].errors}> <%= Enum.reduce(opts, msg, fn {key, value}, acc -> String.replace(acc, "%{#{key}}", fn _ -> to_string(value) end) end) %>
""" end def tabs(assigns) do ~H"""
Goal Trigger
<.custom_events_tab selected?={@selected_tab == "custom_events"} myself={@myself} /> <.pageviews_tab selected?={@selected_tab == "pageviews"} myself={@myself} />
""" end defp custom_events_tab(assigns) do ~H""" Custom Event """ end def pageviews_tab(assigns) do ~H""" Pageview """ end def handle_event("switch-tab", %{"tab" => tab}, socket) do socket = socket |> assign(:selected_tab, tab) |> update(:tab_sequence_id, &(&1 + 1)) {:noreply, socket} end def handle_event("save-goal", %{"goal" => goal}, socket) do case Plausible.Goals.create(socket.assigns.site, goal) do {:ok, goal} -> socket = goal |> Map.put(:funnels, []) |> socket.assigns.on_save_goal.(socket) {:noreply, socket} {:error, %Ecto.Changeset{} = changeset} -> {:noreply, assign(socket, form: to_form(changeset))} end end def handle_event("autoconfigure", _params, socket) do {:noreply, socket.assigns.on_autoconfigure.(socket)} end def suggest_page_paths(input, site) do query = Plausible.Stats.Query.from(site, %{"with_imported" => "true", "period" => "all"}) site |> Plausible.Stats.filter_suggestions(query, "page", input) |> Enum.map(fn %{label: label, value: value} -> {label, value} end) end def suggest_event_names(input, site, existing_goals) do existing_names = existing_goals |> Enum.reject(&is_nil(&1.event_name)) |> Enum.map(& &1.event_name) site |> Plausible.Stats.GoalSuggestions.suggest_event_names(input, exclude: existing_names) |> Enum.map(fn name -> {name, name} end) end defp suffix(context_unique_id, tab_sequence_id) do "#{context_unique_id}-tabseq#{tab_sequence_id}" end end