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
10 changes: 5 additions & 5 deletions lib/plausible/teams/billing.ex
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ defmodule Plausible.Teams.Billing do
@doc """
Returns the number of sites the given team owns.
"""
@spec site_usage(Teams.Team.t()) :: non_neg_integer()
@spec site_usage(Teams.Team.t() | nil) :: non_neg_integer()
def site_usage(nil), do: 0

def site_usage(team) do
Expand Down Expand Up @@ -350,7 +350,7 @@ defmodule Plausible.Teams.Billing do
team owns. Alternatively, given an optional argument of `site_ids`, the usage from
across all those sites is queried instead.
"""
@spec monthly_pageview_usage(Teams.Team.t(), list() | nil) :: monthly_pageview_usage()
@spec monthly_pageview_usage(Teams.Team.t() | nil, list() | nil) :: monthly_pageview_usage()
def monthly_pageview_usage(team, site_ids \\ nil)

def monthly_pageview_usage(team, nil) do
Expand All @@ -376,7 +376,7 @@ defmodule Plausible.Teams.Billing do
end
end

@spec team_member_usage(Teams.Team.t(), Keyword.t()) :: non_neg_integer()
@spec team_member_usage(Teams.Team.t() | nil, Keyword.t()) :: non_neg_integer()
@doc """
Returns the total count of team members associated with the team's sites.

Expand Down Expand Up @@ -430,7 +430,7 @@ defmodule Plausible.Teams.Billing do
pageviews: pageviews,
custom_events: custom_events,
total: pageviews + custom_events,
sites: per_site_usage(owned_site_ids, date_range)
per_site: per_site_usage(owned_site_ids, date_range)
}
end

Expand Down Expand Up @@ -470,7 +470,7 @@ defmodule Plausible.Teams.Billing do
pageviews: pageviews,
custom_events: custom_events,
total: pageviews + custom_events,
sites: per_site_usage(owned_site_ids, date_range)
per_site: per_site_usage(owned_site_ids, date_range)
}
end

Expand Down
64 changes: 58 additions & 6 deletions lib/plausible_web/components/billing/billing.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ defmodule PlausibleWeb.Components.Billing do
use PlausibleWeb, :component
use Plausible

import PlausibleWeb.Components.Icons

require Plausible.Billing.Subscription.Status
alias Plausible.Billing.{Plan, Plans, EnterprisePlan}

Expand Down Expand Up @@ -49,6 +51,10 @@ defmodule PlausibleWeb.Components.Billing do
"""
end

attr :usage, :map, required: true
attr :limit, :any, required: true
attr :total_pageview_usage_domain, :string, default: nil

def render_monthly_pageview_usage(%{usage: usage} = assigns)
when is_map_key(usage, :last_30_days) do
~H"""
Expand All @@ -57,6 +63,7 @@ defmodule PlausibleWeb.Components.Billing do
limit={@limit}
period={:last_30_days}
expanded={true}
total_pageview_usage_domain={@total_pageview_usage_domain}
/>
"""
end
Expand All @@ -75,20 +82,23 @@ defmodule PlausibleWeb.Components.Billing do
usage={@usage.current_cycle}
limit={@limit}
period={:current_cycle}
expanded={not @show_all and Enum.empty?(@usage.current_cycle.sites)}
expanded={not @show_all and Enum.empty?(@usage.current_cycle.per_site)}
total_pageview_usage_domain={@total_pageview_usage_domain}
/>
<%= if @show_all do %>
<.monthly_pageview_usage_breakdown
usage={@usage.last_cycle}
limit={@limit}
period={:last_cycle}
expanded={false}
total_pageview_usage_domain={@total_pageview_usage_domain}
/>
<.monthly_pageview_usage_breakdown
usage={@usage.penultimate_cycle}
limit={@limit}
period={:penultimate_cycle}
expanded={false}
total_pageview_usage_domain={@total_pageview_usage_domain}
/>
<% end %>
</div>
Expand All @@ -99,8 +109,19 @@ defmodule PlausibleWeb.Components.Billing do
attr(:limit, :any, required: true)
attr(:period, :atom, required: true)
attr(:expanded, :boolean, required: true)
attr(:total_pageview_usage_domain, :string, default: nil)

defp monthly_pageview_usage_breakdown(assigns) do
assigns =
assign(
assigns,
:total_link,
dashboard_url(
assigns.total_pageview_usage_domain,
assigns.usage.date_range
)
)

~H"""
<div class="flex flex-col gap-3" x-data={"{ open: #{@expanded} }"}>
<div class="flex flex-col gap-2">
Expand All @@ -120,6 +141,17 @@ defmodule PlausibleWeb.Components.Billing do
class="size-4 transition-transform"
x-bind:class="open ? 'rotate-90' : ''"
/> Total billable pageviews
<.tooltip :if={@total_link} centered?={true}>
<:tooltip_content>View billing period in dashboard</:tooltip_content>
<.link
href={@total_link}
class="text-indigo-500 hover:text-indigo-600"
data-test-id="total-pageviews-dashboard-link"
x-on:click.stop
>
<.external_link_icon class="ml-0.5 size-3.5 [&_path]:stroke-2" />
</.link>
</.tooltip>
</span>
<span class="ml-5 text-sm font-medium text-gray-900 dark:text-gray-100">
{PlausibleWeb.TextHelpers.number_format(@usage.total)}
Expand All @@ -130,23 +162,34 @@ defmodule PlausibleWeb.Components.Billing do
<div x-show="open" class="flex flex-col gap-3 text-sm text-gray-900 dark:text-gray-100">
<.pageview_usage_row
id={"pageviews_#{@period}"}
label={if Enum.empty?(@usage.sites), do: "Pageviews", else: "Total pageviews"}
label={if Enum.empty?(@usage.per_site), do: "Pageviews", else: "Total pageviews"}
value={@usage.pageviews}
/>
<.pageview_usage_row
id={"custom_events_#{@period}"}
label={if Enum.empty?(@usage.sites), do: "Custom events", else: "Total custom events"}
label={if Enum.empty?(@usage.per_site), do: "Custom events", else: "Total custom events"}
value={@usage.custom_events}
/>
<div
:if={not Enum.empty?(@usage.sites)}
:if={not Enum.empty?(@usage.per_site)}
id={"per_site_breakdown_#{@period}"}
class="flex flex-col gap-3 border-t border-gray-200 dark:border-gray-700 pt-3 pl-5"
>
<div :for={{site, index} <- Enum.with_index(@usage.sites)} class="flex flex-col gap-3">
<div :for={{site, index} <- Enum.with_index(@usage.per_site)} class="flex flex-col gap-3">
<hr :if={index > 0} class="border-gray-200 dark:border-gray-700" />
<div class="flex justify-between flex-wrap font-medium">
<span class="truncate">{site.domain}</span>
<span class="flex items-center gap-1 min-w-0">
<span class="truncate">{site.domain}</span>
<.tooltip centered?={true}>
<:tooltip_content>View billing period in dashboard</:tooltip_content>
<.link
href={dashboard_url(site.domain, @usage.date_range)}
class="shrink-0 text-indigo-500 hover:text-indigo-600"
>
<.external_link_icon class="ml-0.5 size-3.5 [&_path]:stroke-2" />
</.link>
</.tooltip>
</span>
<span class="shrink-0">{PlausibleWeb.TextHelpers.number_format(site.total)}</span>
</div>
<.pageview_usage_row label="Pageviews" value={site.pageviews} />
Expand Down Expand Up @@ -174,6 +217,15 @@ defmodule PlausibleWeb.Components.Billing do
"""
end

defp dashboard_url(nil, _date_range), do: nil

defp dashboard_url(domain, date_range) do
base = Routes.stats_path(PlausibleWeb.Endpoint, :stats, domain, [])

base <>
"?period=custom&from=#{Date.to_iso8601(date_range.first)}&to=#{Date.to_iso8601(date_range.last)}"
end

defp cycle_label(:current_cycle), do: "(current cycle)"
defp cycle_label(:last_30_days), do: "(last 30 days)"

Expand Down
20 changes: 19 additions & 1 deletion lib/plausible_web/controllers/settings_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ defmodule PlausibleWeb.SettingsController do

notification_type = Plausible.Billing.Quota.usage_notification_type(team, usage)

total_pageview_usage_domain =
if site_usage == 1 do
[site] = Plausible.Teams.owned_sites(team)
site.domain
else
on_ee(do: team && get_consolidated_view_domain(team), else: nil)
end

render(conn, :subscription,
layout: {PlausibleWeb.LayoutView, :settings},
subscription: subscription,
Expand All @@ -162,7 +170,8 @@ defmodule PlausibleWeb.SettingsController do
site_limit: Teams.Billing.site_limit(team),
team_member_limit: Teams.Billing.team_member_limit(team),
team_member_usage: team_member_usage,
notification_type: notification_type
notification_type: notification_type,
total_pageview_usage_domain: total_pageview_usage_domain
)
end

Expand Down Expand Up @@ -461,6 +470,15 @@ defmodule PlausibleWeb.SettingsController do
end
end

on_ee do
defp get_consolidated_view_domain(team) do
case Plausible.ConsolidatedView.get(team) do
nil -> nil
view -> if Plausible.ConsolidatedView.ok_to_display?(team), do: view.domain
end
end
end

defp handle_email_updated(conn) do
conn
|> put_flash(:success, "Email updated")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
<PlausibleWeb.Components.Billing.render_monthly_pageview_usage
usage={@pageview_usage}
limit={@pageview_limit}
total_pageview_usage_domain={@total_pageview_usage_domain}
/>
</div>
</.tile>
Expand Down
20 changes: 11 additions & 9 deletions test/plausible/billing/quota_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -973,15 +973,15 @@ defmodule Plausible.Billing.QuotaTest do
build(:event, timestamp: ~N[2023-05-15 00:00:00], name: "pageview")
])

%{sites: sites} = Plausible.Teams.Billing.usage_cycle(team, :last_30_days, nil, today)
%{per_site: per_site} = Plausible.Teams.Billing.usage_cycle(team, :last_30_days, nil, today)

assert length(sites) == 2
assert length(per_site) == 2

assert %{pageviews: 1, custom_events: 1, total: 2} =
Enum.find(sites, &(&1.domain == site1.domain))
Enum.find(per_site, &(&1.domain == site1.domain))

assert %{pageviews: 1, custom_events: 0, total: 1} =
Enum.find(sites, &(&1.domain == site2.domain))
Enum.find(per_site, &(&1.domain == site2.domain))
end

test "sites with zero events in the period still appear in the breakdown" do
Expand All @@ -995,12 +995,12 @@ defmodule Plausible.Billing.QuotaTest do
build(:event, timestamp: ~N[2023-05-15 00:00:00], name: "pageview")
])

%{sites: sites} = Plausible.Teams.Billing.usage_cycle(team, :last_30_days, nil, today)
%{per_site: per_site} = Plausible.Teams.Billing.usage_cycle(team, :last_30_days, nil, today)

assert length(sites) == 2
assert length(per_site) == 2

assert %{pageviews: 0, custom_events: 0, total: 0} =
Enum.find(sites, &(&1.domain == site2.domain))
Enum.find(per_site, &(&1.domain == site2.domain))
end

test "returns empty sites list when team has only one site" do
Expand All @@ -1009,7 +1009,8 @@ defmodule Plausible.Billing.QuotaTest do
team = team_of(user)
today = ~D[2023-06-01]

assert %{sites: []} = Plausible.Teams.Billing.usage_cycle(team, :last_30_days, nil, today)
assert %{per_site: []} =
Plausible.Teams.Billing.usage_cycle(team, :last_30_days, nil, today)
end

test "returns empty sites list when team has more than 10 sites" do
Expand All @@ -1018,7 +1019,8 @@ defmodule Plausible.Billing.QuotaTest do
team = team_of(user)
today = ~D[2023-06-01]

assert %{sites: []} = Plausible.Teams.Billing.usage_cycle(team, :last_30_days, nil, today)
assert %{per_site: []} =
Plausible.Teams.Billing.usage_cycle(team, :last_30_days, nil, today)
end
end

Expand Down
Loading
Loading