diff --git a/CHANGELOG.md b/CHANGELOG.md index f9264ca7e2e6..882286bfe3ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## Unreleased ### Added +- UI to edit funnels - Add a search functionality in all Details views, except for Goal Conversions, Countries, Regions, Cities and Google Search terms - Icons for browsers plausible/analytics#4239 - Automatic custom property selection in the dashboard Properties report diff --git a/assets/tailwind.config.js b/assets/tailwind.config.js index e665c42f87f4..ec7657dab822 100644 --- a/assets/tailwind.config.js +++ b/assets/tailwind.config.js @@ -6,8 +6,7 @@ module.exports = { "./js/**/*.js", "../lib/*_web.ex", "../lib/*_web/**/*.*ex", - "../extra/*_web.ex", - "../extra/*_web/**/*.*ex" + "../extra/**/*.*ex", ], safelist: [ // PlausibleWeb.StatsView.stats_container_class/1 uses this class @@ -52,9 +51,9 @@ module.exports = { plugins: [ require('@tailwindcss/forms'), require('@tailwindcss/aspect-ratio'), - plugin(({addVariant}) => addVariant("phx-no-feedback", [".phx-no-feedback&", ".phx-no-feedback &"])), - plugin(({addVariant}) => addVariant("phx-click-loading", [".phx-click-loading&", ".phx-click-loading &"])), - plugin(({addVariant}) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])), - plugin(({addVariant}) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"])), + plugin(({ addVariant }) => addVariant("phx-no-feedback", [".phx-no-feedback&", ".phx-no-feedback &"])), + plugin(({ addVariant }) => addVariant("phx-click-loading", [".phx-click-loading&", ".phx-click-loading &"])), + plugin(({ addVariant }) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])), + plugin(({ addVariant }) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"])), ] } diff --git a/config/dev.exs b/config/dev.exs index 5b80c4c199b5..e1ede12af03b 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -15,6 +15,9 @@ config :plausible, PlausibleWeb.Endpoint, ] ], live_reload: [ + dirs: [ + "extra" + ], patterns: [ ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$}, ~r"lib/plausible_web/(controllers|live|components|templates|views|plugs)/.*(ex|heex)$" diff --git a/extra/lib/plausible/funnel.ex b/extra/lib/plausible/funnel.ex index 208fec2afe42..abd3724f8737 100644 --- a/extra/lib/plausible/funnel.ex +++ b/extra/lib/plausible/funnel.ex @@ -46,7 +46,8 @@ defmodule Plausible.Funnel do has_many :steps, Step, preload_order: [ asc: :step_order - ] + ], + on_replace: :delete has_many :goals, through: [:steps, :goal] timestamps() @@ -56,21 +57,19 @@ defmodule Plausible.Funnel do funnel |> cast(attrs, [:name]) |> validate_required([:name]) - |> cast_assoc(:steps, with: &Step.changeset/2, required: true) + |> put_steps(attrs[:steps] || attrs["steps"]) |> validate_length(:steps, min: @min_steps, max: @max_steps) - |> put_step_orders() |> unique_constraint(:name, name: :funnels_name_site_id_index ) end - def put_step_orders(changeset) do - if steps = Ecto.Changeset.get_change(changeset, :steps) do - steps - |> Enum.with_index(fn step, step_order -> - Ecto.Changeset.put_change(step, :step_order, step_order + 1) - end) - |> then(&Ecto.Changeset.put_change(changeset, :steps, &1)) - end + def put_steps(changeset, steps) do + steps + |> Enum.map(&Step.changeset(%Step{}, &1)) + |> Enum.with_index(fn step, step_order -> + Ecto.Changeset.put_change(step, :step_order, step_order + 1) + end) + |> then(&Ecto.Changeset.put_assoc(changeset, :steps, &1)) end end diff --git a/extra/lib/plausible/funnels.ex b/extra/lib/plausible/funnels.ex index 831228cffd4f..84f30295d61a 100644 --- a/extra/lib/plausible/funnels.ex +++ b/extra/lib/plausible/funnels.ex @@ -35,12 +35,35 @@ defmodule Plausible.Funnels do {:error, :invalid_funnel_size} end + @spec update(Funnel.t(), String.t(), [map()]) :: + {:ok, Funnel.t()} + | {:error, Ecto.Changeset.t() | :invalid_funnel_size | :upgrade_required} + def update(funnel, name, steps) do + site = Plausible.Repo.preload(funnel, site: :owner).site + + case Plausible.Billing.Feature.Funnels.check_availability(site.owner) do + {:error, _} = error -> + error + + :ok -> + funnel + |> Funnel.changeset(%{name: name, steps: steps}) + |> Repo.update() + end + end + @spec create_changeset(Plausible.Site.t(), String.t(), [map()]) :: Ecto.Changeset.t() def create_changeset(site, name, steps) do Funnel.changeset(%Funnel{site_id: site.id}, %{name: name, steps: steps}) end + @spec edit_changeset(Plausible.Funnel.t(), String.t(), [map()]) :: + Ecto.Changeset.t() + def edit_changeset(funnel, name, steps) do + Funnel.changeset(funnel, %{name: name, steps: steps}) + end + @spec ephemeral_definition(Plausible.Site.t(), String.t(), [map()]) :: Funnel.t() def ephemeral_definition(site, name, steps) do site diff --git a/extra/lib/plausible_web/live/funnel_settings.ex b/extra/lib/plausible_web/live/funnel_settings.ex index 2a00a028b6b3..a5859f961022 100644 --- a/extra/lib/plausible_web/live/funnel_settings.ex +++ b/extra/lib/plausible_web/live/funnel_settings.ex @@ -30,9 +30,10 @@ defmodule PlausibleWeb.Live.FunnelSettings do assign(socket, domain: domain, displayed_funnels: socket.assigns.all_funnels, - add_funnel?: false, + setup_funnel?: false, filter_text: "", - current_user_id: user_id + current_user_id: user_id, + funnel_id: nil )} end @@ -42,36 +43,37 @@ defmodule PlausibleWeb.Live.FunnelSettings do ~H"""