defmodule PlausibleWeb.Components.Billing.Notice do
@moduledoc false
use PlausibleWeb, :component
require Plausible.Billing.Subscription.Status
alias Plausible.Billing.{Subscription, Plans, Subscriptions}
def active_grace_period(assigns) do
if assigns.enterprise? do
~H"""
"""
else
~H"""
"""
end
end
def dashboard_locked(assigns) do
~H"""
"""
end
attr(:current_team, :any, required: true)
attr(:current_user, :atom, required: true)
attr(:limit, :integer, required: true)
attr(:resource, :string, required: true)
attr(:rest, :global)
def limit_exceeded(assigns) do
~H"""
<.notice
{@rest}
title={"#{account_label(@current_team)} is limited to #{pretty_print_resource_limit(@limit, @resource)}"}
data-test="limit-exceeded-notice"
theme={:gray}
show_icon={false}
>
To add more {@resource},
"""
end
defp pretty_print_resource_limit(1 = _limit, resource_plural) do
"a single #{String.trim_trailing(resource_plural, "s")}"
end
defp pretty_print_resource_limit(limit, resource_plural) do
"#{limit} #{resource_plural}"
end
attr(:subscription, :map, required: true)
attr(:dismissable, :boolean, default: true)
@doc """
Given a user with a cancelled subscription, this component renders a cancelled
subscription notice. If the given user does not have a subscription or it has a
different status, this function returns an empty template.
It also takes a dismissable argument which renders the notice dismissable (with
the help of JavaScript and localStorage). We show a dismissable notice about a
cancelled subscription across the app, but when the user dismisses it, we will
start displaying it in the account settings > subscription section instead.
So it's either shown across the app, or only on the /settings page. Depending
on whether the localStorage flag to dismiss it has been set or not.
"""
def subscription_cancelled(assigns)
def subscription_cancelled(
%{
dismissable: true,
subscription: %Subscription{status: Subscription.Status.deleted()}
} = assigns
) do
~H"""
"""
end
def subscription_cancelled(
%{
dismissable: false,
subscription: %Subscription{status: Subscription.Status.deleted()}
} = assigns
) do
assigns = assign(assigns, :container_id, "local-subscription-cancelled-notice")
~H"""
"""
end
def subscription_cancelled(assigns), do: ~H""
attr(:class, :string, default: "")
attr(:subscription, :any, default: nil)
def subscription_past_due(
%{subscription: %Subscription{status: Subscription.Status.past_due()}} = assigns
) do
~H"""
"""
end
def subscription_past_due(assigns), do: ~H""
attr(:class, :string, default: "")
attr(:subscription, :any, default: nil)
def subscription_paused(
%{subscription: %Subscription{status: Subscription.Status.paused()}} = assigns
) do
~H"""
"""
end
def subscription_paused(assigns), do: ~H""
def upgrade_ineligible(assigns) do
~H"""
"""
end
def pending_site_ownerships_notice(%{pending_ownership_count: count} = assigns) do
if count > 0 do
message =
"Your account has been invited to become the owner of " <>
if(count == 1, do: "a site, which is", else: "#{count} sites, which are") <>
" being counted towards the usage of your account."
assigns = assign(assigns, message: message)
~H"""
"""
else
~H""
end
end
def growth_grandfathered(assigns) do
~H"""
Your subscription has been grandfathered in at the same rate and terms as when you first joined. If you don't need the "Business" features, you're welcome to stay on this plan. You can adjust the pageview limit or change the billing frequency of this grandfathered plan. If you're interested in business features, you can upgrade to a "Business" plan.
"""
end
@doc """
Renders a usage notification banner based on the notification type.
This is used on the subscription settings page to show contextual
notifications about approaching or exceeded limits.
"""
attr(:type, :atom, required: true)
attr(:team, :any, required: true)
def usage_notification(assigns)
def usage_notification(%{type: :pageview_approaching_limit} = assigns) do
~H"""
<.notice title="You're close to your monthly pageview limit" theme={:gray} show_icon={false}>
No action is required. Occasional traffic spikes are normal, and we'll keep tracking your stats as usual. Upgrading now gives you room to grow if higher traffic continues.
"""
end
def usage_notification(%{type: :team_member_limit_reached} = assigns) do
~H"""
<.notice title="You've reached your current team member limit" theme={:gray} show_icon={false}>
"""
end
def usage_notification(%{type: :site_limit_reached} = assigns) do
~H"""
<.notice title="You've reached your current site limit" theme={:gray} show_icon={false}>
"""
end
def usage_notification(%{type: :site_and_team_member_limit_reached} = assigns) do
~H"""
<.notice
title="You've reached your current limits for team members and sites"
theme={:gray}
show_icon={false}
>
"""
end
def usage_notification(%{type: :traffic_exceeded_current_cycle} = assigns) do
~H"""
<.notice title="Traffic has exceeded your plan limit this cycle" theme={:gray} show_icon={false}>
No action is required. Occasional traffic spikes are normal, but upgrading now gives you room to grow if higher traffic continues.
"""
end
def usage_notification(%{type: :traffic_exceeded_last_cycle} = assigns) do
~H"""
<.notice title="Traffic exceeded your plan limit last cycle" theme={:gray} show_icon={false}>
No action is required. Occasional traffic spikes are normal, but upgrading now gives you room to grow if higher traffic continues.
"""
end
def usage_notification(%{type: :traffic_exceeded_sustained} = assigns) do
~H"""
<.notice title="Upgrade required due to sustained higher traffic" theme={:gray} show_icon={false}>
To ensure uninterrupted access to your stats, please upgrade to a plan that fits your current usage.
"""
end
def usage_notification(%{type: :manual_lock_grace_period_active} = assigns) do
~H"""
<.notice title="You've outgrown your custom plan" theme={:yellow} show_icon={false}>
We'll contact you by email to discuss an updated custom plan based on your current usage.
"""
end
def usage_notification(%{type: :grace_period_active, team: team} = assigns) do
deadline_text =
case Plausible.Teams.GracePeriod.expires_in(team) do
{0, :hours} -> "within the hour"
{1, :hours} -> "within the next hour"
{n, :hours} -> "within the next #{n} hours"
{n, :days} -> "within the next #{n} days"
end
assigns = assign(assigns, :deadline_text, deadline_text)
~H"""
<.notice title="Upgrade required due to sustained higher traffic" theme={:gray} show_icon={false}>
To ensure uninterrupted access to your stats, please upgrade to a plan that fits your current usage {@deadline_text}.
"""
end
def usage_notification(%{type: :dashboard_locked} = assigns) do
~H"""
<.notice title="Dashboard access temporarily locked" theme={:gray} show_icon={false}>
Your stats are still being tracked, but dashboard access is temporarily locked because your site exceeded your plan's pageview limit for two consecutive billing cycles. Upgrade to restore access.
"""
end
def usage_notification(%{type: :trial_ended} = assigns) do
~H"""
<.notice title="Your free trial has ended" theme={:gray} show_icon={false}>
Upgrade to a monthly or yearly plan to continue accessing your sites.
<.button_link
id="upgrade-or-change-plan-link"
href={Routes.billing_path(PlausibleWeb.Endpoint, :choose_plan)}
mt?={false}
>
Choose a plan
"""
end
def usage_notification(assigns), do: ~H""
defp subscription_cancelled_notice_body(assigns) do
~H"""
You have access to your stats until <%= Calendar.strftime(@subscription.next_bill_date, "%b %-d, %Y") %>.
<.link
class="underline inline-block"
href={Routes.billing_path(PlausibleWeb.Endpoint, :choose_plan)}
>
Upgrade your subscription
to make sure you don't lose access.
<.lose_grandfathering_warning subscription={@subscription} />
"""
end
defp lose_grandfathering_warning(%{subscription: subscription} = assigns) do
plan = Plans.get_regular_plan(subscription, only_non_expired: true)
loses_grandfathering? = plan && plan.generation < 5
assigns = assign(assigns, :loses_grandfathering?, loses_grandfathering?)
~H"""
Please also note that by letting your subscription expire, you lose access to our grandfathered terms. If you want to subscribe again after that, your account will be offered the <.link
href="https://plausible.io/#pricing"
target="_blank"
rel="noopener noreferrer"
class="underline"
>latest pricing.
"""
end
defp account_label(%Plausible.Teams.Team{setup_complete: true}), do: "This team"
defp account_label(_team), do: "This account"
end