mirror of
https://github.com/plausible/analytics.git
synced 2024-12-28 12:01:39 +03:00
b3ff695797
* Add Heroicons dependency
* Add name_of/1 html helper
Currently with Floki there's no way to query for
`[name=foo[some]]` selector
* Update changelog
* Make goal deletion possible with only goal id
* Remove stale goal controllers
* Improve ComboBox component
- make sure the list options are always of the parent input width
- allow passing a suggestion function instead of a module
* Stale fixup
* Update routes
* Use the new goals route in funnel settings
* Use a function in the funnel combo
* Use function in the props combo
* Remove old goals form
* Implement new goal settings
* Update moduledoc
* Fix revenue switch in dark mode
* Connect live socket on goal settings page
* Fixup
* Use Heroicons.trash icon
* Tweak goals search input
* Remove unused alias
* Fix search/button alignment
* Fix backspace icon alignment
* Delegate :superadmin check to get_for_user/3
I'll do props settings separately, it's work in progress
in a branch on top of this one already. cc @ukutaht
* Rename socket assigns
* Fixup to 5c9f58e
* Fixup
* Render ComboBox suggestions asynchronously
This commit:
- prevents redundant work by checking the socket connection
- allows passing no options to the ComboBox component,
so that when combined with the `async` option, the options
are asynchronously initialized post-render
- allows updating the suggestions asynchronously with the
`async` option set to `true` - helpful in case of DB
queries used for suggestions
* Update tests
* Throttle comboboxes
* Update tests
* Dim the search input
* Use debounce=200 in ComboBox component
* Move creatable option to the top
* Ensure there's always a leading slash for goals
* Test pageview goals with leading / missing
* Make the modal scrollable on small viewports
179 lines
5.9 KiB
Elixir
179 lines
5.9 KiB
Elixir
defmodule PlausibleWeb.Live.PropsSettings do
|
|
@moduledoc """
|
|
LiveView allowing listing, allowing and disallowing custom event properties.
|
|
"""
|
|
|
|
use Phoenix.LiveView
|
|
use Phoenix.HTML
|
|
alias PlausibleWeb.Live.Components.ComboBox
|
|
|
|
def mount(
|
|
_params,
|
|
%{"site_id" => _site_id, "domain" => domain, "current_user_id" => user_id},
|
|
socket
|
|
) do
|
|
site =
|
|
if Plausible.Auth.is_super_admin?(user_id) do
|
|
Plausible.Sites.get_by_domain(domain)
|
|
else
|
|
Plausible.Sites.get_for_user!(user_id, domain, [:owner, :admin])
|
|
end
|
|
|
|
suggestions =
|
|
site
|
|
|> Plausible.Props.suggest_keys_to_allow()
|
|
|> Enum.map(&{&1, &1})
|
|
|
|
{:ok,
|
|
assign(socket,
|
|
site: site,
|
|
current_user_id: user_id,
|
|
form: new_form(site),
|
|
suggestions: suggestions
|
|
)}
|
|
end
|
|
|
|
def render(assigns) do
|
|
~H"""
|
|
<section id="props-settings-main">
|
|
<.live_component id="embedded_liveview_flash" module={PlausibleWeb.Live.Flash} flash={@flash} />
|
|
|
|
<h1 class="text-normal leading-6 font-medium text-gray-900 dark:text-gray-100">
|
|
Configured properties
|
|
</h1>
|
|
|
|
<h2 class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-400">
|
|
In order for the properties to show up on your dashboard, you need to
|
|
explicitly add them below first
|
|
</h2>
|
|
|
|
<.form :let={f} for={@form} id="props-form" phx-submit="allow" class="mt-5">
|
|
<div class="flex space-x-2">
|
|
<.live_component
|
|
id={:prop_input}
|
|
submit_name="prop"
|
|
class="flex-1"
|
|
module={ComboBox}
|
|
suggest_fun={&ComboBox.StaticSearch.suggest/2}
|
|
options={@suggestions}
|
|
required
|
|
creatable
|
|
/>
|
|
|
|
<button id="allow" type="submit" class="button">+ Add</button>
|
|
</div>
|
|
|
|
<div :if={length(f[:allowed_event_props].errors) > 0} id="prop-errors" role="alert">
|
|
<%= PlausibleWeb.ErrorHelpers.error_tag(f, :allowed_event_props) %>
|
|
</div>
|
|
</.form>
|
|
|
|
<button
|
|
:if={length(@suggestions) > 0}
|
|
title="Use this to add any existing properties from your past events into your settings. This allows you to set up properties without having to manually enter each item."
|
|
class="mt-1 text-sm hover:underline text-indigo-600 dark:text-indigo-400"
|
|
phx-click="allow-existing-props"
|
|
>
|
|
Already sending custom properties? Click to add all existing properties
|
|
</button>
|
|
|
|
<div class="mt-5">
|
|
<%= if is_list(@site.allowed_event_props) && length(@site.allowed_event_props) > 0 do %>
|
|
<ul id="allowed-props" class="divide-gray-200 divide-y dark:divide-gray-600">
|
|
<li
|
|
:for={{prop, index} <- Enum.with_index(@site.allowed_event_props)}
|
|
id={"prop-#{index}"}
|
|
class="flex py-4"
|
|
>
|
|
<span class="flex-1 truncate font-medium text-sm text-gray-800 dark:text-gray-200">
|
|
<%= prop %>
|
|
</span>
|
|
<button
|
|
data-confirm={"Are you sure you want to remove property '#{prop}'? This will just affect the UI, all of your analytics data will stay intact."}
|
|
phx-click="disallow"
|
|
phx-value-prop={prop}
|
|
class="w-4 h-4 text-red-600 hover:text-red-700"
|
|
aria-label={"Remove #{prop} property"}
|
|
>
|
|
<svg
|
|
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"
|
|
aria-hidden="true"
|
|
focusable="false"
|
|
>
|
|
<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>
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
<% else %>
|
|
<p class="text-sm text-gray-800 dark:text-gray-200">
|
|
No properties configured for this site yet
|
|
</p>
|
|
<% end %>
|
|
</div>
|
|
</section>
|
|
"""
|
|
end
|
|
|
|
def handle_event("allow", %{"prop" => prop}, socket) do
|
|
case Plausible.Props.allow(socket.assigns.site, prop) do
|
|
{:ok, site} ->
|
|
send_update(ComboBox, id: :prop_input, display_value: "", submit_value: "")
|
|
|
|
{:noreply,
|
|
assign(socket,
|
|
site: site,
|
|
form: new_form(site),
|
|
suggestions: rebuild_suggestions(socket.assigns.suggestions, site.allowed_event_props)
|
|
)}
|
|
|
|
{:error, changeset} ->
|
|
{:noreply,
|
|
assign(socket,
|
|
form: to_form(Map.put(changeset, :action, :validate))
|
|
)}
|
|
end
|
|
end
|
|
|
|
def handle_event("disallow", %{"prop" => prop}, socket) do
|
|
{:ok, site} = Plausible.Props.disallow(socket.assigns.site, prop)
|
|
{:noreply, assign(socket, site: site)}
|
|
end
|
|
|
|
def handle_event("allow-existing-props", _params, socket) do
|
|
{:ok, site} = Plausible.Props.allow_existing_props(socket.assigns.site)
|
|
|
|
{:noreply,
|
|
assign(socket,
|
|
site: site,
|
|
suggestions: rebuild_suggestions(socket.assigns.suggestions, site.allowed_event_props)
|
|
)}
|
|
end
|
|
|
|
defp rebuild_suggestions(suggestions, allowed_event_props) do
|
|
allowed_event_props = allowed_event_props || []
|
|
|
|
suggestions =
|
|
for {suggestion, _} <- suggestions,
|
|
suggestion not in allowed_event_props,
|
|
do: {suggestion, suggestion}
|
|
|
|
send_update(ComboBox, id: :prop_input, suggestions: suggestions)
|
|
suggestions
|
|
end
|
|
|
|
defp new_form(site) do
|
|
to_form(Plausible.Props.allow_changeset(site, []))
|
|
end
|
|
end
|