defmodule PlausibleWeb.Components.Billing do @moduledoc false use PlausibleWeb, :component use Plausible require Plausible.Billing.Subscription.Status alias Plausible.Billing.{Subscription, Subscriptions, Plan, Plans, EnterprisePlan} attr :current_role, :atom, required: true attr :current_team, :any, required: true attr :locked?, :boolean, required: true slot :inner_block, required: true def feature_gate(assigns) do ~H"""
{render_slot(@inner_block)}
To gain access to this feature, <.upgrade_call_to_action current_role={@current_role} current_team={@current_team} />.
""" end def render_monthly_pageview_usage(%{usage: usage} = assigns) when is_map_key(usage, :last_30_days) do ~H""" <.monthly_pageview_usage_table usage={@usage.last_30_days} limit={@limit} period={:last_30_days} /> """ end def render_monthly_pageview_usage(assigns) do ~H"""
<.title>Monthly pageviews usage
    <.billing_cycle_tab name="Upcoming cycle" tab={:current_cycle} date_range={@usage.current_cycle.date_range} with_separator={true} /> <.billing_cycle_tab name="Last cycle" tab={:last_cycle} date_range={@usage.last_cycle.date_range} with_separator={true} /> <.billing_cycle_tab name="Penultimate cycle" tab={:penultimate_cycle} date_range={@usage.penultimate_cycle.date_range} disabled={@usage.penultimate_cycle.total == 0} />
<.monthly_pageview_usage_table usage={@usage.current_cycle} limit={@limit} period={:current_cycle} />
<.monthly_pageview_usage_table usage={@usage.last_cycle} limit={@limit} period={:last_cycle} />
<.monthly_pageview_usage_table usage={@usage.penultimate_cycle} limit={@limit} period={:penultimate_cycle} />
""" end attr(:usage, :map, required: true) attr(:limit, :any, required: true) attr(:period, :atom, required: true) defp monthly_pageview_usage_table(assigns) do ~H""" <.usage_and_limits_table> <.usage_and_limits_row id={"total_pageviews_#{@period}"} title={"Total billable pageviews#{if @period == :last_30_days, do: " (last 30 days)"}"} usage={@usage.total} limit={@limit} /> <.usage_and_limits_row id={"pageviews_#{@period}"} pad title="Pageviews" usage={@usage.pageviews} /> <.usage_and_limits_row id={"custom_events_#{@period}"} pad title="Custom events" usage={@usage.custom_events} /> """ end attr(:name, :string, required: true) attr(:date_range, :any, required: true) attr(:tab, :atom, required: true) attr(:disabled, :boolean, default: false) attr(:with_separator, :boolean, default: false) defp billing_cycle_tab(assigns) do ~H"""
  • """ end slot(:inner_block, required: true) attr(:rest, :global) def usage_and_limits_table(assigns) do ~H""" {render_slot(@inner_block)}
    """ end attr(:title, :string, required: true) attr(:usage, :integer, required: true) attr(:limit, :integer, default: nil) attr(:pad, :boolean, default: false) attr(:rest, :global) def usage_and_limits_row(assigns) do ~H""" {@title} {PlausibleWeb.TextHelpers.number_format(@usage)} {if is_number(@limit), do: "/ #{PlausibleWeb.TextHelpers.number_format(@limit)}"} """ end def monthly_quota_box(assigns) do ~H"""

    Monthly quota

    {PlausibleWeb.AuthView.subscription_quota(@subscription, format: :long)}
    <.styled_link :if={ not (Plausible.Teams.Billing.enterprise_configured?(@team) && Subscriptions.halted?(@subscription)) } id="#upgrade-or-change-plan-link" href={Routes.billing_path(PlausibleWeb.Endpoint, :choose_plan)} > {change_plan_or_upgrade_text(@subscription)}
    """ end def present_enterprise_plan(assigns) do ~H""" """ end defp present_limit(enterprise_plan, key) do enterprise_plan |> Map.fetch!(key) |> PlausibleWeb.StatsView.large_number_format() end attr :id, :string, required: true attr :paddle_product_id, :string, required: true attr :checkout_disabled, :boolean, default: false attr :user, :map, required: true attr :team, :map, default: nil attr :confirm_message, :any, default: nil slot :inner_block, required: true def paddle_button(assigns) do js_action_expr = start_paddle_checkout_expr(assigns.paddle_product_id, assigns.team, assigns.user) confirmed = if assigns.confirm_message, do: "confirm(\"#{assigns.confirm_message}\")", else: "true" assigns = assigns |> assign(:confirmed, confirmed) |> assign(:js_action_expr, js_action_expr) ~H""" """ end if Mix.env() == :dev do def start_paddle_checkout_expr(paddle_product_id, _team, _user) do "window.location = '#{Routes.dev_subscription_path(PlausibleWeb.Endpoint, :create_form, paddle_product_id)}'" end def paddle_script(assigns), do: ~H"" else def start_paddle_checkout_expr(paddle_product_id, team, user) do passthrough = if team do "ee:#{ee?()};user:#{user.id};team:#{team.id}" else "ee:#{ee?()};user:#{user.id}" end paddle_checkout_params = Jason.encode!(%{ product: paddle_product_id, email: user.email, disableLogout: true, passthrough: passthrough, success: Routes.billing_path(PlausibleWeb.Endpoint, :upgrade_success), theme: "none" }) "Paddle.Checkout.open(#{paddle_checkout_params})" end def paddle_script(assigns) do ~H""" """ end end def upgrade_link(assigns) do ~H""" <.button_link id="upgrade-link-2" href={Routes.billing_path(PlausibleWeb.Endpoint, :choose_plan)} mt?={false} > Upgrade """ end defp change_plan_or_upgrade_text(nil), do: "Upgrade" defp change_plan_or_upgrade_text(%Subscription{status: Subscription.Status.deleted()}), do: "Upgrade" defp change_plan_or_upgrade_text(_subscription), do: "Change plan" def upgrade_call_to_action(assigns) do team = Plausible.Teams.with_subscription(assigns.current_team) upgrade_assistance_required? = case Plans.get_subscription_plan(team && team.subscription) do %Plan{kind: :business} -> true %EnterprisePlan{} -> true _ -> false end cond do not is_nil(assigns.current_role) and assigns.current_role not in [:owner, :billing] -> ~H"please reach out to the team owner to upgrade their subscription" upgrade_assistance_required? -> ~H""" please contact hello@plausible.io to upgrade your subscription """ true -> ~H""" please <.link class="underline inline-block" href={Routes.billing_path(PlausibleWeb.Endpoint, :choose_plan)} > upgrade your subscription """ end end end