-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add usage notifications for billing limits #6056
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
- As a first step towards improving the subscription settings page, this commit adds usage notifications for billing limits. These are not yet being displayed in the UI.
4fe93b2 to
0936c29
Compare
4d89e7b to
db8fa96
Compare
RobertJoonas
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like where we're going with this! As you've probably realized, the billing logic is incredibly complex and there are a lot of different considerations at every step. So even a small PR like this has a bunch of comments 😄
PS: The screen recordings you shared are 💯👌! Excited to see those changes in :)
| 7. Site limit reached | ||
| 8. Team member limit reached | ||
| """ | ||
| def determine_notification_type( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of a single function determining the priority of usage notifications. Makes it easier to display a single notice at a time which makes perfect sense to not overwhelm customers! 👏
Codewise, seeing a few possible improvements here:
- Moving the function
I think this function could move elsewhere, so that this module, located at lib/plausible_web/components only defines actual components.
A suitable place would perhaps be the Plausible.Billing.Quota module, since it's already dealing with usage and limits. The name could also change, perhaps to usage_notification_type?
- We should try to cut down on the number of arguments to make the function signature more readable. Especially in tests.
My proposal is to keep only two arguments: usage_notification_type(team, usage)
Every argument is derivable from team (see the subscription function in PlausibleWeb.SettingsController), but the reason I would keep usage separate is being able to test it more easily. Generating actual usage in tests is cumbersome.
Note that the usage argument would become a map with all the different usage aspects included in it. There's the Plausible.Teams.Billing.quota_usage function that returns everything, including team_members, sites and monthly_pageviews.
So in PlausibleWeb.SettingsController, I imagine we could be passing only the following assigns to the subscription.html.heex template:
def subscription(conn, _params) do
team = conn.assigns.current_team
subscription = Teams.Billing.get_subscription(team)
usage = Teams.Billing.quota_usage(team)
render(conn, :subscription,
layout: {PlausibleWeb.LayoutView, :settings},
team: team,
subscription: subscription,
usage: usage
)
end
| Plausible.Teams.GracePeriod.expired?(team) -> | ||
| :dashboard_locked | ||
|
|
||
| Plausible.Teams.on_trial?(team) == false and is_nil(subscription) -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Plausible.Teams.on_trial?(team) == false and is_nil(subscription) -> | |
| not Plausible.Teams.on_trial?(team) and is_nil(subscription) -> |
| pageview_limit != :unlimited and | ||
| is_map_key(pageview_usage, :last_cycle) and is_map_key(pageview_usage, :penultimate_cycle) and | ||
| Plausible.Billing.Quota.exceeds_last_two_usage_cycles?(pageview_usage, pageview_limit) -> | ||
| :traffic_exceeded_sustained | ||
|
|
||
| is_map_key(pageview_usage, :last_cycle) and | ||
| pageview_usage.last_cycle.total > pageview_limit and | ||
| pageview_limit != :unlimited -> | ||
| :traffic_exceeded_last_cycle | ||
|
|
||
| pageview_limit != :unlimited and is_map_key(pageview_usage, :current_cycle) and | ||
| pageview_usage.current_cycle.total >= pageview_limit * 0.9 -> | ||
| :pageview_approaching_limit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seeing an opportunity to 1) extract some predicate functions and 2) merge these three cond branches into a single one that handles billing cycles in a separate function.
Also, the pageview limit is only :unlimited for trials, so instead of checking whether the pageview_limit is unlimited, we could be checking whether the team is on trial, with the already existing predicate function. Here's what I'm thinking:
cond do
# ...
not Plausible.Teams.on_trial?(team) and has_billing_cycles?(usage.monthly_pageviews) ->
pageview_cycle_usage_notification_type(usage.monthly_pageviews, monthly_pageview_limit)
# ...
end
defp pageview_cycle_usage_notification_type(usage, limit) do
exceeded = Plausible.Billing.Quota.exceeded_cycles(usage, limit)
cond do
:penultimate_cycle in exceeded and :last_cycle_exceeded ->
:traffic_exceeded_sustained
:last_cycle in exceeded ->
:traffic_exceeded_last_cycle
usage.current_cycle >= limit * 0.9 ->
:pageview_approaching_limit
end
endThe has_billing_cycles?/1 function could be a public predicate function in the Plausible.Billing.Quota module, and could also be used in the exceeds_monthly_pageview_limit? function (where the behaviour depends on whether the usage is in billing cycles or last 30 days). Using predicates like that is a good practice as it'll give us a better overview in the code about where this specific logic matters.
About the pageview_cycle_usage_notification_type function -- I think we should opt to use the actual limit checking function that also takes the allowance_margins into account (10% by default). E.g. currently, in the situation where both penultimate and last cycles are exceeded by less than 10% (e.g. 10,150/10,000 and 10,999/10,000), the exceeds_last_two_usage_cycles? would return false, while usage.last_cycle > 10,000 would return true. That said though, I'm not fully sure how to handle these margins.
|
|
||
| site_usage >= site_limit and site_limit != :unlimited and | ||
| team_member_usage >= team_member_limit and team_member_limit != :unlimited -> | ||
| :limits_reached_combined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WDYT about renaming this to :site_and_team_member_limit_reached? Currently, "combined" makes it sound a bit like pageview limit is reached too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good idea!
- Move `determine_notification_type` function from Notice component to `Plausible.Billing.Quota` - Rename `determine_notification_type` to `usage_notification_type` - Cut down on the number of arguments to make the `usage_notification_type` function more readable - Add public `has_billing_cycles` predicate function - Update `exceeds_last_two_usage_cycles` to use `has_billing_cycles` - Extract `pageview_cycle_usage_notification_type` for pageview limit notifications - Rename :limits_reached_combined to :site_and_team_member_limit_reached - Update all tests to use realistic data structures
Changes
Tests
Changelog
Documentation
Dark mode