Display tooltips on plan change when limits exceeded (#4048)

* Reapply "Display upgrade tooltips for exceeded limits (#4032)"

This reverts commit 76e910d45c.

* Switch to alpinejs controlled tooltips

Co-authored-by: Robert Joonas <robertjoonas16@gmail.com>

* Remove unused selector

* Refactor plan limits warning and extract tooltip component

* Remove redundant check

---------

Co-authored-by: Robert Joonas <robertjoonas16@gmail.com>
Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com>
This commit is contained in:
hq1 2024-04-29 11:16:15 +02:00 committed by GitHub
parent b2009aa158
commit ad9141a9d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 99 additions and 5 deletions

View File

@ -164,6 +164,7 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
]) ])
subscription_deleted = Subscription.Status.deleted?(subscription) subscription_deleted = Subscription.Status.deleted?(subscription)
usage_check = check_usage_within_plan_limits(assigns)
{checkout_disabled, disabled_message} = {checkout_disabled, disabled_message} =
cond do cond do
@ -173,7 +174,7 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
change_plan_link_text == "Currently on this plan" && not subscription_deleted -> change_plan_link_text == "Currently on this plan" && not subscription_deleted ->
{true, nil} {true, nil}
assigns.available && !usage_within_plan_limits?(assigns) -> usage_check != :ok ->
{true, "Your usage exceeds this plan"} {true, "Your usage exceeds this plan"}
billing_details_expired -> billing_details_expired ->
@ -183,6 +184,15 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
{false, nil} {false, nil}
end end
exceeded_plan_limits =
case usage_check do
{:error, {:over_plan_limits, limits}} ->
limits
_ ->
[]
end
features_to_lose = assigns.usage.features -- assigns.plan_to_render.features features_to_lose = assigns.usage.features -- assigns.plan_to_render.features
assigns = assigns =
@ -191,6 +201,7 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
|> assign(:change_plan_link_text, change_plan_link_text) |> assign(:change_plan_link_text, change_plan_link_text)
|> assign(:checkout_disabled, checkout_disabled) |> assign(:checkout_disabled, checkout_disabled)
|> assign(:disabled_message, disabled_message) |> assign(:disabled_message, disabled_message)
|> assign(:exceeded_plan_limits, exceeded_plan_limits)
|> assign(:confirm_message, losing_features_message(features_to_lose)) |> assign(:confirm_message, losing_features_message(features_to_lose))
~H""" ~H"""
@ -201,13 +212,37 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
Upgrade Upgrade
</PlausibleWeb.Components.Billing.paddle_button> </PlausibleWeb.Components.Billing.paddle_button>
<% end %> <% end %>
<p :if={@disabled_message} class="h-0 text-center text-sm text-red-700 dark:text-red-500"> <p
:if={@disabled_message}
class="h-0 text-center text-sm text-red-700 dark:text-red-500 disabled-message"
>
<%= if @exceeded_plan_limits != [] do %>
<PlausibleWeb.Components.Generic.tooltip>
<%= @disabled_message %> <%= @disabled_message %>
<:tooltip_content>
Your usage exceeds the following limit(s):<br /><br />
<p :for={limit <- @exceeded_plan_limits}>
<%= Phoenix.Naming.humanize(limit) %><br />
</p>
</:tooltip_content>
</PlausibleWeb.Components.Generic.tooltip>
<% else %>
<%= @disabled_message %>
<% end %>
</p> </p>
""" """
end end
defp usage_within_plan_limits?(%{usage: usage, user: user, plan_to_render: plan}) do defp check_usage_within_plan_limits(%{available: false}) do
{:error, :plan_unavailable}
end
defp check_usage_within_plan_limits(%{
available: true,
usage: usage,
user: user,
plan_to_render: plan
}) do
# At this point, the user is *not guaranteed* to have a `trial_expiry_date`, # At this point, the user is *not guaranteed* to have a `trial_expiry_date`,
# because in the past we've let users upgrade without that constraint, as # because in the past we've let users upgrade without that constraint, as
# well as transfer sites to those accounts. to these accounts we won't be # well as transfer sites to those accounts. to these accounts we won't be
@ -232,7 +267,7 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
[] []
end end
Quota.ensure_within_plan_limits(usage, plan, limit_checking_opts) == :ok Quota.ensure_within_plan_limits(usage, plan, limit_checking_opts)
end end
defp get_paddle_product_id(%Plan{monthly_product_id: plan_id}, :monthly), do: plan_id defp get_paddle_product_id(%Plan{monthly_product_id: plan_id}, :monthly), do: plan_id

View File

@ -318,6 +318,32 @@ defmodule PlausibleWeb.Components.Generic do
""" """
end end
slot :inner_block, required: true
slot :tooltip_content, required: true
def tooltip(assigns) do
~H"""
<div x-data="{sticky: false, hovered: false}" class="tooltip-wrapper relative">
<p
x-on:click="sticky = true; hovered = true"
x-on:click.outside="sticky = false; hovered = false"
x-on:mouseover="hovered = true"
x-on:mouseout="hovered = false"
class="cursor-pointer text-sm text-red-700 dark:text-red-500 mt-1 flex justify-center align-items-center"
>
<%= render_slot(@inner_block) %>
<Heroicons.information_circle class="w-5 h-5 ml-2" />
</p>
<span
x-show="hovered || sticky"
class="bg-gray-900 pointer-events-none absolute bottom-10 margin-x-auto left-10 right-10 transition-opacity p-4 rounded text-white"
>
<%= render_slot(List.first(@tooltip_content)) %>
</span>
</div>
"""
end
attr :rest, :global, include: ~w(fill stroke stroke-width) attr :rest, :global, include: ~w(fill stroke stroke-width)
attr :name, :atom, required: true attr :name, :atom, required: true
attr :outline, :boolean, default: true attr :outline, :boolean, default: true

View File

@ -19,6 +19,7 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
@slider_value "#slider-value" @slider_value "#slider-value"
@growth_plan_box "#growth-plan-box" @growth_plan_box "#growth-plan-box"
@growth_plan_tooltip "#growth-plan-box .tooltip-wrapper span"
@growth_price_tag_amount "#growth-price-tag-amount" @growth_price_tag_amount "#growth-price-tag-amount"
@growth_price_tag_interval "#growth-price-tag-interval" @growth_price_tag_interval "#growth-price-tag-interval"
@growth_highlight_pill "#{@growth_plan_box} #highlight-pill" @growth_highlight_pill "#{@growth_plan_box} #highlight-pill"
@ -243,6 +244,7 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
refute class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" refute class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none"
refute class_of_element(doc, @business_checkout_button) =~ "pointer-events-none" refute class_of_element(doc, @business_checkout_button) =~ "pointer-events-none"
refute element_exists?(doc, @growth_plan_tooltip)
generate_usage_for(site, 1) generate_usage_for(site, 1)
@ -251,6 +253,9 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none"
assert class_of_element(doc, @business_checkout_button) =~ "pointer-events-none" assert class_of_element(doc, @business_checkout_button) =~ "pointer-events-none"
assert text_of_element(doc, @growth_plan_tooltip) ==
"Your usage exceeds the following limit(s): Monthly pageview limit"
end end
test "allows upgrade to a 10k plan with a pageview allowance margin of 0.3 when trial ended 10 days ago", test "allows upgrade to a 10k plan with a pageview allowance margin of 0.3 when trial ended 10 days ago",
@ -497,6 +502,9 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
assert text_of_element(doc, @growth_plan_box) =~ "Your usage exceeds this plan" assert text_of_element(doc, @growth_plan_box) =~ "Your usage exceeds this plan"
assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none"
assert text_of_element(doc, @growth_plan_tooltip) ==
"Your usage exceeds the following limit(s): Team member limit"
end end
test "checkout is disabled when sites usage exceeds rendered plan limit", %{ test "checkout is disabled when sites usage exceeds rendered plan limit", %{
@ -509,6 +517,31 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
assert text_of_element(doc, @growth_plan_box) =~ "Your usage exceeds this plan" assert text_of_element(doc, @growth_plan_box) =~ "Your usage exceeds this plan"
assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none"
assert text_of_element(doc, @growth_plan_tooltip) ==
"Your usage exceeds the following limit(s): Site limit"
end
test "when more than one limit is exceeded, the tooltip enumerates them", %{
conn: conn,
user: user
} do
for _ <- 1..11, do: insert(:site, members: [user])
insert(:site,
memberships: [
build(:site_membership, user: user, role: :owner),
build(:site_membership, user: build(:user)),
build(:site_membership, user: build(:user)),
build(:site_membership, user: build(:user)),
build(:site_membership, user: build(:user))
]
)
{:ok, _lv, doc} = get_liveview(conn)
assert text_of_element(doc, @growth_plan_tooltip) =~ "Team member limit"
assert text_of_element(doc, @growth_plan_tooltip) =~ "Site limit"
end end
test "checkout is not disabled when pageview usage exceeded but next upgrade allowed by override", test "checkout is not disabled when pageview usage exceeded but next upgrade allowed by override",