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"
/>
"""
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"""
"""
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