This commit is contained in:
Adam Rutkowski 2023-05-17 22:11:00 +02:00
parent b2a546ea92
commit c7a6124c3f
7 changed files with 230 additions and 94 deletions

View File

@ -1,4 +1,5 @@
[ [
plugins: [Phoenix.LiveView.HTMLFormatter],
import_deps: [:ecto, :ecto_sql, :phoenix], import_deps: [:ecto, :ecto_sql, :phoenix],
subdirectories: ["priv/*/migrations"], subdirectories: ["priv/*/migrations"],
inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"] inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"]

View File

@ -29,7 +29,7 @@ window.addEventListener(`phx:update-value`, (e) => {
window.addEventListener(`phx:hide`, (e) => { window.addEventListener(`phx:hide`, (e) => {
let el = document.getElementById(e.detail.id) let el = document.getElementById(e.detail.id)
el.style.display = 'none' el.classList.add('hidden')
}) })
window.addEventListener(`phx:scroll-to`, (e) => { window.addEventListener(`phx:scroll-to`, (e) => {

View File

@ -20,17 +20,31 @@ defmodule PlausibleWeb.Live.FunnelSettings do
def render(assigns) do def render(assigns) do
~H""" ~H"""
<%= if @add_funnel? do %> <%= if @add_funnel? do %>
<.live_component module={PlausibleWeb.Live.FunnelSettings.Form} id="funnelForm" site={@site} form={to_form(Plausible.Funnel.changeset())} goals={@goals} /> <.live_component
module={PlausibleWeb.Live.FunnelSettings.Form}
id="funnelForm"
site={@site}
form={to_form(Plausible.Funnel.changeset())}
goals={@goals}
/>
<% else %> <% else %>
<div :if={Enum.count(@goals) >= 2}> <div :if={Enum.count(@goals) >= 2}>
<.live_component module={PlausibleWeb.Live.FunnelSettings.List} id="funnelsList" funnels={@funnels} site={@site} /> <.live_component
module={PlausibleWeb.Live.FunnelSettings.List}
id="funnelsList"
funnels={@funnels}
site={@site}
/>
<button type="button" class="button mt-6" phx-click="add_funnel">+ Add funnel</button> <button type="button" class="button mt-6" phx-click="add_funnel">+ Add funnel</button>
</div> </div>
<div :if={Enum.count(@goals) < 2}> <div :if={Enum.count(@goals) < 2}>
<div class="rounded-md bg-yellow-100 p-4 mt-8"> <div class="rounded-md bg-yellow-100 p-4 mt-8">
<p class="text-sm leading-5 text-gray-900 dark:text-gray-100"> <p class="text-sm leading-5 text-gray-900 dark:text-gray-100">
You need to define at least two goals to create a funnel. Go ahead and <%= link "add goals", to: PlausibleWeb.Router.Helpers.site_path(@socket, :new_goal, @site.domain), class: "text-indigo-500 w-full text-center" %> to proceed. You need to define at least two goals to create a funnel. Go ahead and <%= link(
"add goals",
to: PlausibleWeb.Router.Helpers.site_path(@socket, :new_goal, @site.domain),
class: "text-indigo-500 w-full text-center"
) %> to proceed.
</p> </p>
</div> </div>
</div> </div>

View File

@ -16,11 +16,21 @@ defmodule PlausibleWeb.Live.FunnelSettings.Form do
~H""" ~H"""
<div class="grid grid-cols-4 gap-6 mt-6"> <div class="grid grid-cols-4 gap-6 mt-6">
<div class="col-span-4 sm:col-span-2"> <div class="col-span-4 sm:col-span-2">
<.form :let={f} for={@form} phx-change="validate" phx-submit="save" onkeydown="return event.key != 'Enter';"> <.form
<%= label f, "Funnel name", class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %> :let={f}
for={@form}
phx-change="validate"
phx-submit="save"
onkeydown="return event.key != 'Enter';"
>
<%= label(f, "Funnel name",
class: "block text-sm font-medium text-gray-700 dark:text-gray-300"
) %>
<.input field={@form[:name]} /> <.input field={@form[:name]} />
<%= label f, "Funnel Steps", class: "mt-6 block text-sm font-medium text-gray-700 dark:text-gray-300" %> <%= label(f, "Funnel Steps",
class: "mt-6 block text-sm font-medium text-gray-700 dark:text-gray-300"
) %>
<.live_component <.live_component
:for={step_number <- 1..@step_count} :for={step_number <- 1..@step_count}
@ -29,11 +39,24 @@ defmodule PlausibleWeb.Live.FunnelSettings.Form do
options={Enum.map(@goals, fn goal -> {goal.id, Plausible.Goal.display_name(goal)} end)} options={Enum.map(@goals, fn goal -> {goal.id, Plausible.Goal.display_name(goal)} end)}
/> />
<a :if={@step_count < 5} class="underline text-indigo-600 text-sm cursor-pointer mt-6" phx-click="add-step" phx-target={@myself}>+ Add another step</a> <a
:if={@step_count < 5}
class="underline text-indigo-600 text-sm cursor-pointer mt-6"
phx-click="add-step"
phx-target={@myself}
>
+ Add another step
</a>
<div class="mt-6"> <div class="mt-6">
<button type="submit" class="button mt-6">Save</button> <button type="submit" class="button mt-6">Save</button>
<button type="button" class="inline-block mt-4 ml-2 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150 " phx-click="cancel_add_funnel">Cancel</button> <button
type="button"
class="inline-block mt-4 ml-2 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150 "
phx-click="cancel_add_funnel"
>
Cancel
</button>
</div> </div>
</.form> </.form>
</div> </div>
@ -45,7 +68,15 @@ defmodule PlausibleWeb.Live.FunnelSettings.Form do
def input(assigns) do def input(assigns) do
~H""" ~H"""
<input autofocus type="text" id={@field.id} name={@field.name} value={@field.value} phx-debounce="300" class="focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-900 dark:text-gray-300 block w-full rounded-md sm:text-sm border-gray-300 dark:border-gray-500" /> <input
autofocus
type="text"
id={@field.id}
name={@field.name}
value={@field.value}
phx-debounce="300"
class="focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-900 dark:text-gray-300 block w-full rounded-md sm:text-sm border-gray-300 dark:border-gray-500"
/>
""" """
end end

View File

@ -18,9 +18,9 @@ defmodule PlausibleWeb.Live.FunnelSettings.InputPicker do
{:ok, socket} {:ok, socket}
end end
attr :placeholder, :string, default: "Select option or search by typing" attr(:placeholder, :string, default: "Select option or search by typing")
attr :id, :any, default: nil attr(:id, :any, default: nil)
attr :options, :list, required: true attr(:options, :list, required: true)
def render(assigns) do def render(assigns) do
~H""" ~H"""
@ -28,8 +28,8 @@ defmodule PlausibleWeb.Live.FunnelSettings.InputPicker do
<div class="relative w-full"> <div class="relative w-full">
<div <div
phx-click-away={close_dropdown(@id)} phx-click-away={close_dropdown(@id)}
class="pl-2 pr-8 py-1 w-full dark:bg-gray-900 dark:text-gray-300 rounded-md shadow-sm border border-gray-300 dark:border-gray-700 focus-within:border-indigo-500 focus-within:ring-1 focus-within:ring-indigo-500"> class="pl-2 pr-8 py-1 w-full dark:bg-gray-900 dark:text-gray-300 rounded-md shadow-sm border border-gray-300 dark:border-gray-700 focus-within:border-indigo-500 focus-within:ring-1 focus-within:ring-indigo-500"
>
<input <input
type="text" type="text"
autocomplete="off" autocomplete="off"
@ -40,7 +40,9 @@ defmodule PlausibleWeb.Live.FunnelSettings.InputPicker do
phx-target={@myself} phx-target={@myself}
name={"display-#{@id}"} name={"display-#{@id}"}
value={@display_value} value={@display_value}
class="border-none py-1 px-1 p-0 w-full inline-block rounded-md focus:outline-none focus:ring-0 text-sm" style="background-color: inherit;" /> class="border-none py-1 px-1 p-0 w-full inline-block rounded-md focus:outline-none focus:ring-0 text-sm"
style="background-color: inherit;"
/>
<.dropdown_anchor id={@id} /> <.dropdown_anchor id={@id} />
@ -48,83 +50,122 @@ defmodule PlausibleWeb.Live.FunnelSettings.InputPicker do
</div> </div>
</div> </div>
<.dropdown ref={@id} options={@options} choices={@choices} target={@myself} candidate={@candidate} /> <.dropdown
ref={@id}
options={@options}
choices={@choices}
target={@myself}
candidate={@candidate}
/>
</div> </div>
""" """
end end
attr :id, :any, required: true attr(:id, :any, required: true)
def dropdown_anchor(assigns) do def dropdown_anchor(assigns) do
~H""" ~H"""
<div phx-click={open_dropdown(@id)} class="cursor-pointer absolute inset-y-0 right-0 flex items-center pr-2"> <div
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="h-4 w-4 text-gray-500"> phx-click={open_dropdown(@id)}
<path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd"></path> class="cursor-pointer absolute inset-y-0 right-0 flex items-center pr-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
class="h-4 w-4 text-gray-500"
>
<path
fill-rule="evenodd"
d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z"
clip-rule="evenodd"
>
</path>
</svg> </svg>
</div> </div>
""" """
end end
def open_dropdown(js \\ %JS{}, id) do def open_dropdown(js \\ %JS{}, id) do
js |> close_all_dropdowns() |> JS.show(to: "#dropdown-#{id}") js
|> close_all_dropdowns()
|> JS.remove_class("hidden", to: "#dropdown-#{id}")
end end
def close_dropdown(js \\ %JS{}, id) do def close_dropdown(js \\ %JS{}, id) do
JS.hide(js, to: "#dropdown-#{id}") JS.add_class(js, "hidden", to: "#dropdown-#{id}")
end end
def close_all_dropdowns(js \\ %JS{}) do def close_all_dropdowns(js \\ %JS{}) do
JS.hide(js, to: ".dropdown") JS.add_class(js, "hidden", to: ".dropdown")
end end
attr :ref, :string, required: true attr(:ref, :string, required: true)
attr :options, :list, default: [] attr(:options, :list, default: [])
attr :choices, :list, default: [] attr(:choices, :list, default: [])
attr :candidate, :integer, required: true attr(:candidate, :integer, required: true)
attr :target, :any attr(:target, :any)
def dropdown(assigns) do def dropdown(assigns) do
~H""" ~H"""
<ul <ul
tabindex="-1"
id={"dropdown-#{@ref}"} id={"dropdown-#{@ref}"}
phx-target={@target} class="dropdown hidden z-50 absolute mt-1 max-h-60 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm dark:bg-gray-900"
class="dropdown hidden z-50 absolute mt-1 max-h-60 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm dark:bg-gray-900"> >
<.option :if={@choices != []} :for={{{submit_value, display_value}, idx} <- Enum.with_index(@choices)} <.option
:for={{{submit_value, display_value}, idx} <- Enum.with_index(@choices)}
:if={@choices != []}
idx={idx} idx={idx}
submit_value={submit_value} submit_value={submit_value}
display_value={display_value} display_value={display_value}
target={@target} target={@target}
ref={@ref} ref={@ref}
candidate={@candidate} /> candidate={@candidate}
/>
<div :if={@choices == []} class="relative cursor-default select-none py-2 px-4 text-gray-700 dark:text-gray-300"> <div
:if={@choices == []}
class="relative cursor-default select-none py-2 px-4 text-gray-700 dark:text-gray-300"
>
No matches found. Try searching for something different. No matches found. Try searching for something different.
</div> </div>
</ul> </ul>
""" """
end end
attr :display_value, :string, required: true attr(:display_value, :string, required: true)
attr :submit_value, :integer, required: true attr(:submit_value, :integer, required: true)
attr :ref, :string, required: true attr(:ref, :string, required: true)
attr :target, :any attr(:target, :any)
attr :idx, :integer, required: true attr(:idx, :integer, required: true)
attr :candidate, :integer, required: true attr(:candidate, :integer, required: true)
def option(assigns) do def option(assigns) do
assigns = assign(assigns, :max_options_displayed, @max_options_displayed) assigns = assign(assigns, :max_options_displayed, @max_options_displayed)
~H""" ~H"""
<li class={["relative select-none py-2 px-3 cursor-pointer dark:text-gray-300", @idx == @candidate && "text-white bg-indigo-500"]} <li
id={"dropdown-#{@ref}-option-#{@idx}"}> class={[
"relative select-none py-2 px-3 cursor-pointer dark:text-gray-300",
<a phx-click={select_option(@ref, @submit_value, @display_value)} phx-value-display-value={@display_value} phx-target={@target}> @idx == @candidate && "text-white bg-indigo-500"
]}
id={"dropdown-#{@ref}-option-#{@idx}"}
>
<a
phx-click={select_option(@ref, @submit_value, @display_value)}
phx-value-display-value={@display_value}
phx-target={@target}
>
<span class="block truncate"> <span class="block truncate">
<%= @display_value %> <%= @display_value %>
</span> </span>
</a> </a>
</li> </li>
<li :if={@idx == @max_options_displayed - 1} class="text-xs text-gray-500 relative py-2 px-3">Max results reached. Refine your search by typing in goal name.</li> <li :if={@idx == @max_options_displayed - 1} class="text-xs text-gray-500 relative py-2 px-3">
Max results reached. Refine your search by typing in goal name.
</li>
""" """
end end

View File

@ -9,9 +9,26 @@ defmodule PlausibleWeb.Live.FunnelSettings.List do
<div class="mt-4"> <div class="mt-4">
<%= for funnel <- @funnels do %> <%= for funnel <- @funnels do %>
<div class="border-b border-gray-300 dark:border-gray-500 py-3 flex justify-between"> <div class="border-b border-gray-300 dark:border-gray-500 py-3 flex justify-between">
<span class="text-sm font-medium text-gray-900 dark:text-gray-100"><%= funnel.name %></span> <span class="text-sm font-medium text-gray-900 dark:text-gray-100">
<%= funnel.name %>
</span>
<%= button(to: "", method: :delete, class: "text-sm text-red-600", data: [confirm: "Are you sure you want to remove funnel '#{funnel.name}'? This will just affect the UI, all of your analytics data will stay intact."]) do %> <%= button(to: "", method: :delete, class: "text-sm text-red-600", data: [confirm: "Are you sure you want to remove funnel '#{funnel.name}'? This will just affect the UI, all of your analytics data will stay intact."]) do %>
<svg class="feather feather-sm" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg> <svg
class="feather feather-sm"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="3 6 5 6 21 6"></polyline>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2">
</path>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>

View File

@ -57,14 +57,46 @@ defmodule PlausibleWeb.SiteView do
def google_logo(assigns \\ %{}) do def google_logo(assigns \\ %{}) do
~H""" ~H"""
<svg width="46px" height="46px" viewBox="0 0 46 46" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> <svg
width="46px"
height="46px"
viewBox="0 0 46 46"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
>
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-1.000000, -1.000000)"> <g transform="translate(-1.000000, -1.000000)">
<g transform="translate(15.000000, 15.000000)"> <g transform="translate(15.000000, 15.000000)">
<path d="M17.64,9.20454545 C17.64,8.56636364 17.5827273,7.95272727 17.4763636,7.36363636 L9,7.36363636 L9,10.845 L13.8436364,10.845 C13.635,11.97 13.0009091,12.9231818 12.0477273,13.5613636 L12.0477273,15.8195455 L14.9563636,15.8195455 C16.6581818,14.2527273 17.64,11.9454545 17.64,9.20454545 L17.64,9.20454545 Z" id="Shape" fill="#4285F4" sketch:type="MSShapeGroup"></path> <path
<path d="M9,18 C11.43,18 13.4672727,17.1940909 14.9563636,15.8195455 L12.0477273,13.5613636 C11.2418182,14.1013636 10.2109091,14.4204545 9,14.4204545 C6.65590909,14.4204545 4.67181818,12.8372727 3.96409091,10.71 L0.957272727,10.71 L0.957272727,13.0418182 C2.43818182,15.9831818 5.48181818,18 9,18 L9,18 Z" id="Shape" fill="#34A853" sketch:type="MSShapeGroup"></path> d="M17.64,9.20454545 C17.64,8.56636364 17.5827273,7.95272727 17.4763636,7.36363636 L9,7.36363636 L9,10.845 L13.8436364,10.845 C13.635,11.97 13.0009091,12.9231818 12.0477273,13.5613636 L12.0477273,15.8195455 L14.9563636,15.8195455 C16.6581818,14.2527273 17.64,11.9454545 17.64,9.20454545 L17.64,9.20454545 Z"
<path d="M3.96409091,10.71 C3.78409091,10.17 3.68181818,9.59318182 3.68181818,9 C3.68181818,8.40681818 3.78409091,7.83 3.96409091,7.29 L3.96409091,4.95818182 L0.957272727,4.95818182 C0.347727273,6.17318182 0,7.54772727 0,9 C0,10.4522727 0.347727273,11.8268182 0.957272727,13.0418182 L3.96409091,10.71 L3.96409091,10.71 Z" id="Shape" fill="#FBBC05" sketch:type="MSShapeGroup"></path> id="Shape"
<path d="M9,3.57954545 C10.3213636,3.57954545 11.5077273,4.03363636 12.4404545,4.92545455 L15.0218182,2.34409091 C13.4631818,0.891818182 11.4259091,0 9,0 C5.48181818,0 2.43818182,2.01681818 0.957272727,4.95818182 L3.96409091,7.29 C4.67181818,5.16272727 6.65590909,3.57954545 9,3.57954545 L9,3.57954545 Z" id="Shape" fill="#EA4335" sketch:type="MSShapeGroup"></path> fill="#4285F4"
sketch:type="MSShapeGroup"
>
</path>
<path
d="M9,18 C11.43,18 13.4672727,17.1940909 14.9563636,15.8195455 L12.0477273,13.5613636 C11.2418182,14.1013636 10.2109091,14.4204545 9,14.4204545 C6.65590909,14.4204545 4.67181818,12.8372727 3.96409091,10.71 L0.957272727,10.71 L0.957272727,13.0418182 C2.43818182,15.9831818 5.48181818,18 9,18 L9,18 Z"
id="Shape"
fill="#34A853"
sketch:type="MSShapeGroup"
>
</path>
<path
d="M3.96409091,10.71 C3.78409091,10.17 3.68181818,9.59318182 3.68181818,9 C3.68181818,8.40681818 3.78409091,7.83 3.96409091,7.29 L3.96409091,4.95818182 L0.957272727,4.95818182 C0.347727273,6.17318182 0,7.54772727 0,9 C0,10.4522727 0.347727273,11.8268182 0.957272727,13.0418182 L3.96409091,10.71 L3.96409091,10.71 Z"
id="Shape"
fill="#FBBC05"
sketch:type="MSShapeGroup"
>
</path>
<path
d="M9,3.57954545 C10.3213636,3.57954545 11.5077273,4.03363636 12.4404545,4.92545455 L15.0218182,2.34409091 C13.4631818,0.891818182 11.4259091,0 9,0 C5.48181818,0 2.43818182,2.01681818 0.957272727,4.95818182 L3.96409091,7.29 C4.67181818,5.16272727 6.65590909,3.57954545 9,3.57954545 L9,3.57954545 Z"
id="Shape"
fill="#EA4335"
sketch:type="MSShapeGroup"
>
</path>
<path d="M0,0 L18,0 L18,18 L0,18 L0,0 Z" id="Shape" sketch:type="MSShapeGroup"></path> <path d="M0,0 L18,0 L18,18 L0,18 L0,0 Z" id="Shape" sketch:type="MSShapeGroup"></path>
</g> </g>
</g> </g>