Prepare business tier for release (#3464)

* Change limits for trials

* Keep legacy trial limits for users that registered before the business tier

* Change private preview notice for release

* Run formatter

* Add countdown to private preview notice
This commit is contained in:
Vini Brasil 2023-11-08 09:51:34 -03:00 committed by GitHub
parent 38b1834b3f
commit c9bf5827e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 118 additions and 48 deletions

View File

@ -101,8 +101,8 @@ defmodule Plausible.Billing.Feature do
@impl true
def check_availability(%Plausible.Auth.User{} = user) do
cond do
not FunWithFlags.enabled?(:business_tier, for: user) -> :ok
Keyword.get(unquote(opts), :free) -> :ok
FunWithFlags.enabled?(:premium_features_private_preview) -> :ok
__MODULE__ in Quota.allowed_features_for(user) -> :ok
true -> {:error, :upgrade_required}
end

View File

@ -27,7 +27,8 @@ defmodule Plausible.Billing.Plans do
Module.put_attribute(__MODULE__, :external_resource, path)
end
@business_tier_launch ~N[2023-12-01 12:00:00]
@business_tier_launch ~N[2023-11-08 12:00:00]
def business_tier_launch, do: @business_tier_launch
@spec growth_plans_for(User.t()) :: [Plan.t()]
@doc """

View File

@ -39,16 +39,28 @@ defmodule Plausible.Billing.Quota do
end
end
@site_limit_for_trials 50
@site_limit_for_trials 10
@site_limit_for_legacy_trials 50
@site_limit_for_free_10k 50
defp get_site_limit_from_plan(user) do
user = Plausible.Users.with_subscription(user)
case Plans.get_subscription_plan(user.subscription) do
%EnterprisePlan{} -> :unlimited
%Plan{site_limit: site_limit} -> site_limit
:free_10k -> @site_limit_for_free_10k
nil -> @site_limit_for_trials
%EnterprisePlan{} ->
:unlimited
%Plan{site_limit: site_limit} ->
site_limit
:free_10k ->
@site_limit_for_free_10k
nil ->
if Timex.before?(user.inserted_at, Plans.business_tier_launch()) do
@site_limit_for_legacy_trials
else
@site_limit_for_trials
end
end
end
@ -101,7 +113,8 @@ defmodule Plausible.Billing.Quota do
|> Tuple.sum()
end
@team_member_limit_for_trials 5
@team_member_limit_for_trials 3
@team_member_limit_for_legacy_trials :unlimited
@spec team_member_limit(Plausible.Auth.User.t()) :: non_neg_integer()
@doc """
Returns the limit of team members a user can have in their sites.
@ -110,10 +123,21 @@ defmodule Plausible.Billing.Quota do
user = Plausible.Users.with_subscription(user)
case Plans.get_subscription_plan(user.subscription) do
%EnterprisePlan{} -> :unlimited
%Plan{team_member_limit: limit} -> limit
:free_10k -> :unlimited
nil -> @team_member_limit_for_trials
%EnterprisePlan{} ->
:unlimited
%Plan{team_member_limit: limit} ->
limit
:free_10k ->
:unlimited
nil ->
if Timex.before?(user.inserted_at, Plans.business_tier_launch()) do
@team_member_limit_for_legacy_trials
else
@team_member_limit_for_trials
end
end
end

View File

@ -55,8 +55,7 @@ defmodule Plausible.Sites do
)
|> maybe_filter_by_domain(domain_filter)
result =
Repo.paginate(sites_query, pagination_params)
result = Repo.paginate(sites_query, pagination_params)
entries =
Enum.map(result.entries, fn

View File

@ -231,8 +231,7 @@ defmodule Plausible.Stats.Clickhouse do
%{total_visitors: total, site_id: site_id}, acc -> Map.put_new(acc, site_id, total)
end)
total_q =
visitors_24h_total(now, -24, 0, site_id_to_domain_mapping)
total_q = visitors_24h_total(now, -24, 0, site_id_to_domain_mapping)
current_q =
from(

View File

@ -5,7 +5,7 @@ defmodule PlausibleWeb.Components.Billing do
import PlausibleWeb.Components.Generic
require Plausible.Billing.Subscription.Status
alias PlausibleWeb.Router.Helpers, as: Routes
alias Plausible.Billing.Subscription
alias Plausible.{Billing.Subscription, Billing.Plans}
attr(:billable_user, Plausible.Auth.User, required: true)
attr(:current_user, Plausible.Auth.User, required: true)
@ -16,21 +16,27 @@ defmodule PlausibleWeb.Components.Billing do
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
def premium_feature_notice(assigns) do
private_preview? = not FunWithFlags.enabled?(:business_tier, for: assigns.current_user)
billable_user = Plausible.Users.with_subscription(assigns.billable_user)
plan = Plans.get_regular_plan(billable_user.subscription, only_non_expired: true)
growth? = plan && plan.kind == :growth
private_preview? = FunWithFlags.enabled?(:premium_features_private_preview)
display_upgrade_link? = assigns.current_user.id == assigns.billable_user.id
has_access? = assigns.feature_mod.check_availability(assigns.billable_user) == :ok
message =
cond do
private_preview? && assigns.grandfathered? ->
"#{assigns.feature_mod.display_name()} is an upcoming premium functionality that's free-to-use during the private preview. Existing subscribers will be grandfathered and will keep having access to this feature without any change to their plan."
private_preview? ->
"#{assigns.feature_mod.display_name()} is an upcoming premium functionality that's free-to-use during the private preview. Pricing will be announced soon."
Plausible.Billing.on_trial?(assigns.billable_user) ->
"#{assigns.feature_mod.display_name()} is part of the Plausible Business plan. You can access it during your trial, but you'll need to subscribe to the Business plan to retain access after the trial ends."
private_preview? && display_upgrade_link? && growth? ->
~H"""
Business plans are now live! The private preview of <%= @feature_mod.display_name() %> for Plausible Growth plans ends <%= private_preview_days_remaining() %>. If you wish to continue using this feature, please
<.link class="underline" href={Routes.billing_path(PlausibleWeb.Endpoint, :upgrade)}>
upgrade your subscription
</.link> to the Plausible Business plan.
"""
not has_access? && display_upgrade_link? ->
~H"""
<%= @feature_mod.display_name() %> is part of the Plausible Business plan. To get access to it, please
@ -55,6 +61,18 @@ defmodule PlausibleWeb.Components.Billing do
"""
end
defp private_preview_days_remaining do
private_preview_ends_at = Timex.shift(Plausible.Billing.Plans.business_tier_launch(), days: 7)
days_remaining = Timex.diff(private_preview_ends_at, NaiveDateTime.utc_now(), :day)
if days_remaining <= 0 do
"today"
else
"in #{days_remaining} days"
end
end
slot(:inner_block, required: true)
attr(:rest, :global)

View File

@ -15,10 +15,10 @@ defmodule PlausibleWeb.Api.ExternalSitesController do
{:error, :limit, limit, _} ->
conn
|> put_status(403)
|> put_status(402)
|> json(%{
error:
"Your account has reached the limit of #{limit} sites per account. Please contact hello@plausible.io to unlock more sites."
"Your account has reached the limit of #{limit} sites. To unlock more sites, please upgrade your subscription."
})
{:error, _, changeset, _} ->

View File

@ -128,8 +128,7 @@ defmodule PlausibleWeb.BillingController do
business_tier_enabled? = FunWithFlags.enabled?(:business_tier, for: user)
with {:ok, {subscription, preview_info}} <- preview_subscription(user, new_plan_id) do
back_action =
if business_tier_enabled?, do: :choose_plan, else: :change_plan_form
back_action = if business_tier_enabled?, do: :choose_plan, else: :change_plan_form
render(conn, "change_plan_preview.html",
back_link: Routes.billing_path(conn, back_action),

View File

@ -73,8 +73,7 @@ defmodule PlausibleWeb.Live.Components.Visitors do
defp scale(data, target_range) do
max_value = Enum.max_by(data, fn %{visitors: visitors} -> visitors end)
scaling_factor =
if max_value.visitors > 0, do: target_range / max_value.visitors, else: 0
scaling_factor = if max_value.visitors > 0, do: target_range / max_value.visitors, else: 0
Enum.map(data, fn %{visitors: visitors} ->
round(target_range - visitors * scaling_factor)

View File

@ -41,8 +41,23 @@ defmodule Plausible.Billing.QuotaTest do
assert :unlimited == Quota.site_limit(user)
end
test "returns 50 when user in on trial" do
user = insert(:user, trial_expiry_date: Timex.shift(Timex.now(), days: 7))
test "returns 10 when user in on trial" do
user =
insert(:user,
trial_expiry_date: Timex.shift(Timex.now(), days: 7),
inserted_at: ~U[2024-01-01T00:00:00Z]
)
assert 10 == Quota.site_limit(user)
end
test "returns 50 when user in on trial but registered before the business tier was live" do
user =
insert(:user,
trial_expiry_date: Timex.shift(Timex.now(), days: 7),
inserted_at: ~U[2023-10-01T00:00:00Z]
)
assert 50 == Quota.site_limit(user)
end
@ -56,14 +71,15 @@ defmodule Plausible.Billing.QuotaTest do
assert 50 == Quota.site_limit(user)
end
test "returns 50 for enterprise users who have not upgraded yet and are on trial" do
test "returns 10 for enterprise users who have not upgraded yet and are on trial" do
user =
insert(:user,
enterprise_plan: build(:enterprise_plan, paddle_plan_id: "123321"),
subscription: nil
subscription: nil,
inserted_at: ~U[2024-01-01T00:00:00Z]
)
assert 50 == Quota.site_limit(user)
assert 10 == Quota.site_limit(user)
end
test "is unlimited for enterprise customers" do
@ -371,8 +387,23 @@ defmodule Plausible.Billing.QuotaTest do
end
test "returns 5 when user in on trial" do
user = insert(:user, trial_expiry_date: Timex.shift(Timex.now(), days: 7))
assert 5 == Quota.team_member_limit(user)
user =
insert(:user,
trial_expiry_date: Timex.shift(Timex.now(), days: 7),
inserted_at: ~U[2024-01-01T00:00:00Z]
)
assert 3 == Quota.team_member_limit(user)
end
test "returns unlimited when user in on trial but registered before the business tier was live" do
user =
insert(:user,
trial_expiry_date: Timex.shift(Timex.now(), days: 7),
inserted_at: ~U[2023-10-01T00:00:00Z]
)
assert :unlimited == Quota.team_member_limit(user)
end
test "is unlimited for enterprise customers" do

View File

@ -67,7 +67,7 @@ defmodule Plausible.Site.Memberships.CreateInvitationTest do
end
test "returns error when owner is over their team member limit" do
[owner, inviter, invitee] = insert_list(3, :user)
[owner, inviter, invitee] = insert_list(3, :user, inserted_at: ~N[2024-01-01T00:00:00Z])
memberships =
[
@ -77,7 +77,7 @@ defmodule Plausible.Site.Memberships.CreateInvitationTest do
site = insert(:site, memberships: memberships)
assert {:error, {:over_limit, 5}} =
assert {:error, {:over_limit, 3}} =
CreateInvitation.create_invitation(site, inviter, invitee.email, :viewer)
end

View File

@ -93,8 +93,7 @@ defmodule Plausible.SitesTest do
page_number: 1,
total_entries: 0,
total_pages: 1
} =
Sites.list(user, %{})
} = Sites.list(user, %{})
end
test "returns invitations and sites" do

View File

@ -2,11 +2,12 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do
use PlausibleWeb.ConnCase, async: false
use Plausible.Repo
setup %{conn: conn} do
user = insert(:user)
setup :create_user
setup %{conn: conn, user: user} do
api_key = insert(:api_key, user: user, scopes: ["sites:provision:*"])
conn = Plug.Conn.put_req_header(conn, "authorization", "Bearer #{api_key.key}")
{:ok, user: user, api_key: api_key, conn: conn}
{:ok, api_key: api_key, conn: conn}
end
describe "POST /api/v1/sites" do
@ -70,9 +71,9 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do
"timezone" => "Europe/Tallinn"
})
assert json_response(conn, 403) == %{
assert json_response(conn, 402) == %{
"error" =>
"Your account has reached the limit of 50 sites per account. Please contact hello@plausible.io to unlock more sites."
"Your account has reached the limit of 10 sites. To unlock more sites, please upgrade your subscription."
}
end

View File

@ -44,7 +44,7 @@ defmodule PlausibleWeb.Site.MembershipControllerTest do
})
assert html_response(conn, 200) =~
"Your account is limited to 5 team members. You can upgrade your plan to increase this limit."
"Your account is limited to 3 team members. You can upgrade your plan to increase this limit."
end
test "fails to create invitation with insufficient permissions", %{conn: conn, user: user} do

View File

@ -272,7 +272,7 @@ defmodule PlausibleWeb.SiteControllerTest do
assert html = html_response(conn, 200)
assert html =~ "Upgrade required"
assert html =~ "Your account is limited to 50 sites"
assert html =~ "Your account is limited to 10 sites"
assert html =~ "Please contact support"
refute Repo.get_by(Plausible.Site, domain: "over-limit.example.com")
end