Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/plausible_web/components/generic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -485,8 +485,9 @@ defmodule PlausibleWeb.Components.Generic do
attr :feature_toggle?, :boolean, default: false
attr :current_role, :atom, default: nil
attr :current_team, :any, default: nil
attr :current_user, :any, default: nil
attr :site, :any
attr :conn, :any
attr :conn, :any, default: nil
attr :show_content?, :boolean, default: true

def tile(assigns) do
Expand All @@ -506,6 +507,7 @@ defmodule PlausibleWeb.Components.Generic do
feature_mod={@feature_mod}
site={@site}
conn={@conn}
current_user={@current_user}
/>
</header>
<div :if={@show_content?} class="border-b dark:border-gray-700 mx-6"></div>
Expand Down
22 changes: 19 additions & 3 deletions lib/plausible_web/components/site/feature.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ defmodule PlausibleWeb.Components.Site.Feature do

attr(:site, Plausible.Site, required: true)
attr(:feature_mod, :atom, required: true, values: Plausible.Billing.Feature.list())
attr(:conn, Plug.Conn, required: true)
attr(:conn, :any, default: nil)
attr(:current_user, :any, default: nil)
attr(:class, :any, default: nil)
slot(:inner_block)

def toggle(assigns) do
def toggle(%{conn: %Plug.Conn{}} = assigns) do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!💡

assigns =
assigns
|> assign(:current_setting, assigns.feature_mod.enabled?(assigns.site))
Expand All @@ -37,7 +38,22 @@ defmodule PlausibleWeb.Components.Site.Feature do
"""
end

def target(site, setting, conn, set_to) when is_boolean(set_to) do
def toggle(assigns) do
~H"""
<.live_component
module={PlausibleWeb.Components.Site.Feature.ToggleLive}
id={"feature-toggle-#{@site.id}-#{@feature_mod}"}
site={@site}
feature_mod={@feature_mod}
current_user={@current_user}
class={@class}
>
{render_slot(@inner_block)}
</.live_component>
"""
end

defp target(site, setting, conn, set_to) when is_boolean(set_to) do
r = conn.request_path
Routes.site_path(conn, :update_feature_visibility, site.domain, setting, r: r, set: set_to)
end
Expand Down
96 changes: 96 additions & 0 deletions lib/plausible_web/components/site/toggle_live.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
defmodule PlausibleWeb.Components.Site.Feature.ToggleLive do
@moduledoc """
LiveComponent for rendering a user-facing feature toggle in LiveView contexts.
Instead of using form submission, this component messages itself to handle toggles.
"""
use PlausibleWeb, :live_component

def update(assigns, socket) do
site = Plausible.Repo.preload(assigns.site, :team)
current_setting = assigns.feature_mod.enabled?(site)
disabled? = assigns.feature_mod.check_availability(site.team) !== :ok

{:ok,
socket
|> assign(assigns)
|> assign(:site, site)
|> assign(:current_setting, current_setting)
|> assign(:disabled?, disabled?)}
end

def render(assigns) do
~H"""
<div class="mt-4">
<div class={@class}>
<div class="my-2 flex items-center">
<button
type="button"
phx-click="toggle"
phx-target={@myself}
class={[
"relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full transition-colors ease-in-out duration-200",
if(@current_setting, do: "bg-indigo-600", else: "bg-gray-200 dark:bg-gray-600"),
if(@disabled?, do: "cursor-not-allowed")
]}
disabled={@disabled?}
>
<span
aria-hidden="true"
class={[
"inline-block size-5 rounded-full bg-white shadow transform transition ease-in-out duration-200",
if(@current_setting, do: "translate-x-5", else: "translate-x-0")
]}
/>
</button>

<span class={[
"ml-2 font-medium leading-5 text-sm",
if(@disabled?,
do: "text-gray-500 dark:text-gray-400",
else: "text-gray-900 dark:text-gray-100"
)
]}>
Show in dashboard
</span>
</div>
</div>

<div :if={@current_setting}>
{render_slot(@inner_block)}
</div>
</div>
"""
end

def handle_event("toggle", _params, socket) do
site = socket.assigns.site
feature_mod = socket.assigns.feature_mod
current_user = socket.assigns.current_user

case feature_mod.toggle(site, current_user) do
{:ok, updated_site} ->
new_setting = Map.fetch!(updated_site, feature_mod.toggle_field())

message =
if new_setting do
"#{feature_mod.display_name()} are now visible again on your dashboard"
else
"#{feature_mod.display_name()} are now hidden from your dashboard"
end

{:noreply,
socket
|> assign(:site, updated_site)
|> assign(:current_setting, feature_mod.enabled?(updated_site))
|> put_flash(:success, message)}

{:error, _} ->
{:noreply,
socket
|> put_flash(
:error,
"Something went wrong. Failed to toggle #{feature_mod.display_name()} on your dashboard."
)}
end
end
end
10 changes: 0 additions & 10 deletions lib/plausible_web/controllers/site_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -163,19 +163,9 @@ defmodule PlausibleWeb.SiteController do
def settings_visibility(conn, _params) do
site = conn.assigns[:site]

has_shared_links? =
Repo.exists?(
from(l in Plausible.Site.SharedLink,
where:
l.site_id == ^site.id and
l.name not in ^Plausible.Sites.shared_link_special_names()
)
)

conn
|> render("settings_visibility.html",
site: site,
has_shared_links?: has_shared_links?,
dogfood_page_path: "/:dashboard/settings/visibility",
connect_live_socket: true,
layout: {PlausibleWeb.LayoutView, "site_settings.html"}
Expand Down
195 changes: 107 additions & 88 deletions lib/plausible_web/live/shared_link_settings.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ defmodule PlausibleWeb.Live.SharedLinkSettings do
current_user
|> Plausible.Sites.get_for_user!(domain, roles: [:owner, :admin, :editor, :super_admin])
end)
|> assign_new(:site_role, fn %{site: site, current_user: current_user} ->
{:ok, {_, site_role}} = Plausible.Teams.Memberships.site_role(site, current_user)
site_role
end)
|> assign_new(:shared_links, fn %{site: site} ->
Plausible.Repo.all(
from(l in Plausible.Site.SharedLink,
Expand All @@ -41,96 +45,111 @@ defmodule PlausibleWeb.Live.SharedLinkSettings do
<div id="shared-link-settings-main">
<.flash_messages flash={@flash} />

<.live_component
:let={modal_unique_id}
module={Modal}
preload?={false}
id="shared-links-form-modal"
<.tile
docs="shared-links"
feature_mod={Plausible.Billing.Feature.SharedLinks}
site={@site}
current_role={@site_role}
current_team={@current_team}
>
<:title>
Shared links
</:title>
<:subtitle :if={Enum.count(@shared_links) > 0}>
Share your stats privately with anyone. Links are unique, secure, and can be password-protected.
</:subtitle>

<.live_component
module={PlausibleWeb.Live.SharedLinkSettings.Form}
id={"shared-links-form-#{modal_unique_id}"}
context_unique_id={modal_unique_id}
site={@site}
shared_link={@form_shared_link}
on_save_shared_link={
fn shared_link, socket ->
send(self(), {:shared_link_added, shared_link})
Modal.close(socket, "shared-links-form-modal")
end
}
/>
</.live_component>

<%= if Enum.empty?(@shared_links) do %>
<div class="flex flex-col items-center justify-center pt-5 pb-6 max-w-md mx-auto">
<h3 class="text-center text-base font-medium text-gray-900 dark:text-gray-100 leading-7">
Create your first shared link
</h3>
<p class="text-center text-sm mt-1 text-gray-500 dark:text-gray-400 leading-5 text-pretty">
Share your stats privately with anyone. Links are unique, secure, and can be password-protected.
<.styled_link href="https://plausible.io/docs/shared-links" target="_blank">
Learn more
</.styled_link>
</p>
<.button
id="add-shared-link-button"
phx-click="add-shared-link"
x-data
x-on:click={Modal.JS.preopen("shared-links-form-modal")}
class="mt-4"
>
Add shared link
</.button>
</div>
<% else %>
<.filter_bar filtering_enabled?={false}>
<.button
id="add-shared-link-button"
phx-click="add-shared-link"
mt?={false}
x-data
x-on:click={Modal.JS.preopen("shared-links-form-modal")}
>
Add shared link
</.button>
</.filter_bar>

<.table rows={@shared_links} id="shared-links-table">
<:thead>
<.th hide_on_mobile>Name</.th>
<.th>Link</.th>
<.th invisible>Actions</.th>
</:thead>
<:tbody :let={link}>
<.td truncate hide_on_mobile>
{link.name}
<Heroicons.lock_closed :if={link.password_hash} class="feather ml-2 mb-0.5" />
<Heroicons.lock_open :if={!link.password_hash} class="feather ml-2 mb-0.5" />
</.td>
<.td>
<.input_with_clipboard
name={link.slug}
id={link.slug}
value={Plausible.Sites.shared_link_url(@site, link)}
/>
</.td>
<.td actions>
<.edit_button
class="mt-1"
phx-click="edit-shared-link"
phx-value-slug={link.slug}
/>
<.delete_button
class="mt-1"
phx-click="delete-shared-link"
phx-value-slug={link.slug}
data-confirm="Are you sure you want to delete this shared link? The stats will not be accessible with this link anymore."
/>
</.td>
</:tbody>
</.table>
<% end %>
:let={modal_unique_id}
module={Modal}
preload?={false}
id="shared-links-form-modal"
>
<.live_component
module={PlausibleWeb.Live.SharedLinkSettings.Form}
id={"shared-links-form-#{modal_unique_id}"}
context_unique_id={modal_unique_id}
site={@site}
shared_link={@form_shared_link}
on_save_shared_link={
fn shared_link, socket ->
send(self(), {:shared_link_added, shared_link})
Modal.close(socket, "shared-links-form-modal")
end
}
/>
</.live_component>

<%= if Enum.empty?(@shared_links) do %>
<div class="flex flex-col items-center justify-center pt-5 pb-6 max-w-md mx-auto">
<h3 class="text-center text-base font-medium text-gray-900 dark:text-gray-100 leading-7">
Create your first shared link
</h3>
<p class="text-center text-sm mt-1 text-gray-500 dark:text-gray-400 leading-5 text-pretty">
Share your stats privately with anyone. Links are unique, secure, and can be password-protected.
<.styled_link href="https://plausible.io/docs/shared-links" target="_blank">
Learn more
</.styled_link>
</p>
<.button
id="add-shared-link-button"
phx-click="add-shared-link"
x-data
x-on:click={Modal.JS.preopen("shared-links-form-modal")}
class="mt-4"
>
Add shared link
</.button>
</div>
<% else %>
<.filter_bar filtering_enabled?={false}>
<.button
id="add-shared-link-button"
phx-click="add-shared-link"
mt?={false}
x-data
x-on:click={Modal.JS.preopen("shared-links-form-modal")}
>
Add shared link
</.button>
</.filter_bar>

<.table rows={@shared_links} id="shared-links-table">
<:thead>
<.th hide_on_mobile>Name</.th>
<.th>Link</.th>
<.th invisible>Actions</.th>
</:thead>
<:tbody :let={link}>
<.td truncate hide_on_mobile>
{link.name}
<Heroicons.lock_closed :if={link.password_hash} class="feather ml-2 mb-0.5" />
<Heroicons.lock_open :if={!link.password_hash} class="feather ml-2 mb-0.5" />
</.td>
<.td>
<.input_with_clipboard
name={link.slug}
id={link.slug}
value={Plausible.Sites.shared_link_url(@site, link)}
/>
</.td>
<.td actions>
<.edit_button
class="mt-1"
phx-click="edit-shared-link"
phx-value-slug={link.slug}
/>
<.delete_button
class="mt-1"
phx-click="delete-shared-link"
phx-value-slug={link.slug}
data-confirm="Are you sure you want to delete this shared link? The stats will not be accessible with this link anymore."
/>
</.td>
</:tbody>
</.table>
<% end %>
</.tile>
</div>
"""
end
Expand Down
Loading
Loading