diff --git a/lib/plausible/billing/feature.ex b/lib/plausible/billing/feature.ex
index 3ca3706316..c9bd5bb0eb 100644
--- a/lib/plausible/billing/feature.ex
+++ b/lib/plausible/billing/feature.ex
@@ -72,7 +72,6 @@ defmodule Plausible.Billing.Feature do
@features [
Plausible.Billing.Feature.Props,
- Plausible.Billing.Feature.Teams,
Plausible.Billing.Feature.SharedLinks,
Plausible.Billing.Feature.Funnels,
Plausible.Billing.Feature.Goals,
@@ -200,13 +199,6 @@ defmodule Plausible.Billing.Feature.Props do
toggle_field: :props_enabled
end
-defmodule Plausible.Billing.Feature.Teams do
- @moduledoc false
- use Plausible.Billing.Feature,
- name: :teams,
- display_name: "Team Management"
-end
-
defmodule Plausible.Billing.Feature.SharedLinks do
@moduledoc false
use Plausible.Billing.Feature,
@@ -238,3 +230,19 @@ defmodule Plausible.Billing.Feature.SitesAPI do
name: :sites_api,
display_name: "Sites API"
end
+
+defmodule Plausible.Billing.Feature.Teams do
+ @moduledoc """
+ Unlike other feature modules, this one only exists to make feature gating
+ settings views more convenient. Other than that, it's not even considered
+ a feature on its own. The real access to "Teams" is controlled by the
+ team member limit.
+ """
+ def check_availability(team) do
+ if Plausible.Teams.Billing.solo?(team) do
+ {:error, :upgrade_required}
+ else
+ :ok
+ end
+ end
+end
diff --git a/lib/plausible/teams/billing.ex b/lib/plausible/teams/billing.ex
index f04c34320e..153f5298fc 100644
--- a/lib/plausible/teams/billing.ex
+++ b/lib/plausible/teams/billing.ex
@@ -235,8 +235,16 @@ defmodule Plausible.Teams.Billing do
nil -> @team_member_limit_for_trials
end
end
+
+ def solo?(nil), do: true
+
+ def solo?(team) do
+ team_member_limit(team) == 0
+ end
else
def team_member_limit(_team), do: :unlimited
+
+ def solo?(_team), do: false
end
@doc """
@@ -605,19 +613,19 @@ defmodule Plausible.Teams.Billing do
case Plans.get_subscription_plan(team.subscription) do
%EnterprisePlan{features: features} ->
- features ++ [Feature.Teams, SharedLinks]
+ features ++ [SharedLinks]
%Plan{features: features} ->
features
:free_10k ->
- [Goals, Props, StatsAPI, Feature.Teams, SharedLinks]
+ [Goals, Props, StatsAPI, SharedLinks]
nil ->
if Teams.on_trial?(team) do
Feature.list() -- [SitesAPI]
else
- [Goals, Feature.Teams, SharedLinks]
+ [Goals, SharedLinks]
end
end
end
diff --git a/lib/plausible_web/components/billing/billing.ex b/lib/plausible_web/components/billing/billing.ex
index c88d755dd7..31431eaa8b 100644
--- a/lib/plausible_web/components/billing/billing.ex
+++ b/lib/plausible_web/components/billing/billing.ex
@@ -5,21 +5,14 @@ defmodule PlausibleWeb.Components.Billing do
use Plausible
require Plausible.Billing.Subscription.Status
- alias Plausible.Billing.{Subscription, Subscriptions, Feature, Plan, Plans, EnterprisePlan}
+ alias Plausible.Billing.{Subscription, Subscriptions, Plan, Plans, EnterprisePlan}
attr :current_role, :atom, required: true
attr :current_team, :any, required: true
- attr :feature_mod, :atom, required: true, values: Feature.list()
+ attr :locked?, :boolean, required: true
slot :inner_block, required: true
def feature_gate(assigns) do
- assigns =
- assign(
- assigns,
- :locked?,
- assigns.feature_mod.check_availability(assigns.current_team) != :ok
- )
-
~H"""
{render_slot(@inner_block)}
diff --git a/lib/plausible_web/components/billing/plan_benefits.ex b/lib/plausible_web/components/billing/plan_benefits.ex
index 9a2ebb83a5..ac79e558fe 100644
--- a/lib/plausible_web/components/billing/plan_benefits.ex
+++ b/lib/plausible_web/components/billing/plan_benefits.ex
@@ -62,7 +62,8 @@ defmodule PlausibleWeb.Components.Billing.PlanBenefits do
[
"Everything in Starter",
site_limit_benefit(growth_plan),
- team_member_limit_benefit(growth_plan)
+ team_member_limit_benefit(growth_plan),
+ "Team Management"
]
|> Kernel.++(feature_benefits(growth_plan))
|> Kernel.--(starter_benefits)
diff --git a/lib/plausible_web/components/generic.ex b/lib/plausible_web/components/generic.ex
index eafdba2aec..57de8d3cf6 100644
--- a/lib/plausible_web/components/generic.ex
+++ b/lib/plausible_web/components/generic.ex
@@ -472,7 +472,7 @@ defmodule PlausibleWeb.Components.Generic do
<%= if @feature_mod do %>
@@ -634,6 +634,7 @@ defmodule PlausibleWeb.Components.Generic do
slot :subtitle
slot :inner_block, required: true
slot :footer
+ attr :padding?, :boolean, default: true
attr :rest, :global
def focus_box(assigns) do
@@ -642,7 +643,7 @@ defmodule PlausibleWeb.Components.Generic do
class="bg-white w-full max-w-lg mx-auto dark:bg-gray-800 text-gray-900 dark:text-gray-100 shadow-md rounded-md mt-12"
{@rest}
>
-
+
<.title :if={@title != []}>
{render_slot(@title)}
diff --git a/lib/plausible_web/live/team_setup.ex b/lib/plausible_web/live/team_setup.ex
index 563ee0751c..27a0a1f0c6 100644
--- a/lib/plausible_web/live/team_setup.ex
+++ b/lib/plausible_web/live/team_setup.ex
@@ -55,10 +55,12 @@ defmodule PlausibleWeb.Live.TeamSetup do
end
def render(assigns) do
+ assigns = assign(assigns, :locked?, Plausible.Teams.Billing.solo?(assigns.current_team))
+
~H"""
- <.focus_box>
+ <.focus_box padding?={false}>
<:title>
-
+
Create a new team
<.docs_info slug="users-roles" />
@@ -66,39 +68,49 @@ defmodule PlausibleWeb.Live.TeamSetup do
<:subtitle>
- Name your team, add team members and assign roles. When ready, click "Create Team" to send invitations
+
+ Name your team, add team members and assign roles. When ready, click "Create Team" to send invitations
+
- <.form
- :let={f}
- for={@team_name_form}
- method="post"
- phx-change="update-team"
- phx-blur="update-team"
- id="update-team-form"
- class="mt-4 mb-8"
- >
- <.input
- type="text"
- placeholder={"#{@current_user.name}'s Team"}
- autofocus
- field={f[:name]}
- label="Name"
- width="w-full"
- phx-debounce="500"
- />
-
+
+
+ <.form
+ :let={f}
+ for={@team_name_form}
+ method="post"
+ phx-change="update-team"
+ phx-blur="update-team"
+ id="update-team-form"
+ class="mt-4 mb-8"
+ >
+ <.input
+ type="text"
+ placeholder={"#{@current_user.name}'s Team"}
+ autofocus={not @locked?}
+ field={f[:name]}
+ label="Name"
+ width="w-full"
+ phx-debounce="500"
+ />
+
- <.label class="mb-2">
- Team Members
-
- {live_render(@socket, PlausibleWeb.Live.TeamManagement,
- id: "team-management-setup",
- container: {:div, id: "team-setup"},
- session: %{
- "mode" => "team-setup"
- }
- )}
+ <.label class="mb-2">
+ Team Members
+
+ {live_render(@socket, PlausibleWeb.Live.TeamManagement,
+ id: "team-management-setup",
+ container: {:div, id: "team-setup"},
+ session: %{
+ "mode" => "team-setup"
+ }
+ )}
+
+
"""
end
diff --git a/lib/plausible_web/templates/layout/_header.html.heex b/lib/plausible_web/templates/layout/_header.html.heex
index eef1a336b1..c2d6697673 100644
--- a/lib/plausible_web/templates/layout/_header.html.heex
+++ b/lib/plausible_web/templates/layout/_header.html.heex
@@ -92,10 +92,7 @@
Account Settings
-
+
<.dropdown_item class="flex" href={Routes.team_setup_path(@conn, :setup)}>
Create a Team
diff --git a/lib/plausible_web/templates/site/settings_people.html.heex b/lib/plausible_web/templates/site/settings_people.html.heex
index 41b2a420a9..71705e9e22 100644
--- a/lib/plausible_web/templates/site/settings_people.html.heex
+++ b/lib/plausible_web/templates/site/settings_people.html.heex
@@ -1,5 +1,5 @@
<.settings_tiles>
- <%= if not Plausible.Teams.setup?(@site.team) and Plausible.Billing.Feature.Teams.check_availability(@site.team) == :ok do %>
+ <%= if not Plausible.Teams.setup?(@site.team) and not Plausible.Teams.Billing.solo?(@site.team) do %>
<% end %>
diff --git a/priv/plans_v1.json b/priv/plans_v1.json
index d55bbc60f5..065eb209d1 100644
--- a/priv/plans_v1.json
+++ b/priv/plans_v1.json
@@ -11,7 +11,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -27,7 +26,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -43,7 +41,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -59,7 +56,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -75,7 +71,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -91,7 +86,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -107,7 +101,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -123,7 +116,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -139,7 +131,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -155,7 +146,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
}
diff --git a/priv/plans_v2.json b/priv/plans_v2.json
index 61107f95e1..cb24b7d748 100644
--- a/priv/plans_v2.json
+++ b/priv/plans_v2.json
@@ -11,7 +11,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -27,7 +26,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -43,7 +41,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -59,7 +56,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -75,7 +71,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -91,7 +86,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -107,7 +101,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -123,7 +116,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -139,7 +131,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -155,7 +146,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
}
diff --git a/priv/plans_v3.json b/priv/plans_v3.json
index 63a6a67103..55f4a03ee5 100644
--- a/priv/plans_v3.json
+++ b/priv/plans_v3.json
@@ -11,7 +11,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -27,7 +26,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -43,7 +41,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -59,7 +56,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -75,7 +71,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -91,7 +86,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -107,7 +101,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -123,7 +116,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -142,7 +134,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -161,7 +152,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -180,7 +170,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -199,7 +188,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -218,7 +206,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -237,7 +224,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -256,7 +242,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -275,7 +260,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
}
diff --git a/priv/plans_v4.json b/priv/plans_v4.json
index 6a8f2f52d5..36e7f0a352 100644
--- a/priv/plans_v4.json
+++ b/priv/plans_v4.json
@@ -9,7 +9,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -24,7 +23,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -39,7 +37,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -54,7 +51,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -69,7 +65,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -84,7 +79,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -99,7 +93,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -114,7 +107,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -134,7 +126,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -154,7 +145,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -174,7 +164,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -194,7 +183,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -214,7 +202,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -234,7 +221,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -254,7 +240,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -274,7 +259,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
diff --git a/priv/plans_v5.json b/priv/plans_v5.json
index 2705b0585b..0015888680 100644
--- a/priv/plans_v5.json
+++ b/priv/plans_v5.json
@@ -113,7 +113,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -129,7 +128,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -145,7 +143,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -161,7 +158,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -177,7 +173,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -193,7 +188,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -209,7 +203,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -225,7 +218,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -241,7 +233,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -261,7 +252,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -281,7 +271,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -301,7 +290,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -321,7 +309,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -341,7 +328,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -361,7 +347,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -381,7 +366,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs
index 126a7f19d9..bc525553be 100644
--- a/priv/repo/seeds.exs
+++ b/priv/repo/seeds.exs
@@ -13,6 +13,8 @@ use Plausible
import Plausible.Teams.Test
+FunWithFlags.enable(:starter_tier)
+
words =
for i <- 0..(:erlang.system_info(:atom_count) - 1),
do: :erlang.binary_to_term(<<131, 75, i::24>>)
@@ -66,6 +68,11 @@ user2 = new_user(name: "Mary Jane", email: "user2@plausible.test", password: "pl
site2 = new_site(domain: "computer.example.com", owner: user2)
invite_guest(site2, user, inviter: user2, role: :viewer)
+solo_user = new_user(name: "Solo User", email: "solo@plausible.test", password: "plausible")
+new_site(domain: "mysolosite.com", owner: solo_user)
+{:ok, solo_team} = Plausible.Teams.get_or_create(solo_user)
+Plausible.Billing.DevSubscriptions.create(solo_team.id, "910413")
+
Plausible.Factory.insert_list(29, :ip_rule, site: site)
Plausible.Factory.insert(:country_rule, site: site, country_code: "PL")
Plausible.Factory.insert(:country_rule, site: site, country_code: "EE")
diff --git a/priv/sandbox_plans_v1.json b/priv/sandbox_plans_v1.json
index c3d471442c..6b6ded8b60 100644
--- a/priv/sandbox_plans_v1.json
+++ b/priv/sandbox_plans_v1.json
@@ -11,7 +11,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -27,7 +26,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -43,7 +41,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -59,7 +56,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -75,7 +71,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -91,7 +86,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -107,7 +101,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -123,7 +116,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -139,7 +131,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -155,7 +146,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
}
diff --git a/priv/sandbox_plans_v2.json b/priv/sandbox_plans_v2.json
index c48ea0bf6d..e483e59199 100644
--- a/priv/sandbox_plans_v2.json
+++ b/priv/sandbox_plans_v2.json
@@ -11,7 +11,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -27,7 +26,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -43,7 +41,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -59,7 +56,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -75,7 +71,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -91,7 +86,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -107,7 +101,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -123,7 +116,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -139,7 +131,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -155,7 +146,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
}
diff --git a/priv/sandbox_plans_v3.json b/priv/sandbox_plans_v3.json
index 6f83ef8f12..295e159cb0 100644
--- a/priv/sandbox_plans_v3.json
+++ b/priv/sandbox_plans_v3.json
@@ -11,7 +11,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -27,7 +26,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -43,7 +41,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -59,7 +56,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -75,7 +71,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -91,7 +86,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -107,7 +101,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -123,7 +116,6 @@
"goals",
"props",
"stats_api",
- "teams",
"shared_links"
]
},
@@ -142,7 +134,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -161,7 +152,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -180,7 +170,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -199,7 +188,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -218,7 +206,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -237,7 +224,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -256,7 +242,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
},
@@ -275,7 +260,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
]
}
diff --git a/priv/sandbox_plans_v4.json b/priv/sandbox_plans_v4.json
index fe4ae130e8..4d82acb7f2 100644
--- a/priv/sandbox_plans_v4.json
+++ b/priv/sandbox_plans_v4.json
@@ -9,7 +9,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -24,7 +23,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -39,7 +37,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -54,7 +51,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -69,7 +65,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -84,7 +79,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -99,7 +93,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -114,7 +107,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links"
],
"data_retention_in_years": 3
@@ -134,7 +126,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -154,7 +145,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -174,7 +164,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -194,7 +183,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -214,7 +202,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -234,7 +221,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -254,7 +240,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
@@ -274,7 +259,6 @@
"funnels",
"stats_api",
"site_segments",
- "teams",
"shared_links"
],
"data_retention_in_years": 5
diff --git a/priv/sandbox_plans_v5.json b/priv/sandbox_plans_v5.json
index 2a39c79d51..d3d104cc80 100644
--- a/priv/sandbox_plans_v5.json
+++ b/priv/sandbox_plans_v5.json
@@ -113,7 +113,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -129,7 +128,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -145,7 +143,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -161,7 +158,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -177,7 +173,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -193,7 +188,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -209,7 +203,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -225,7 +218,6 @@
"team_member_limit": 3,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments"
],
@@ -241,7 +233,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -261,7 +252,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -281,7 +271,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -301,7 +290,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -321,7 +309,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -341,7 +328,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -361,7 +347,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
@@ -381,7 +366,6 @@
"team_member_limit": 10,
"features": [
"goals",
- "teams",
"shared_links",
"site_segments",
"props",
diff --git a/test/plausible/billing/feature_test.exs b/test/plausible/billing/feature_test.exs
index e189fdc53c..afc9ea0e77 100644
--- a/test/plausible/billing/feature_test.exs
+++ b/test/plausible/billing/feature_test.exs
@@ -30,11 +30,6 @@ defmodule Plausible.Billing.FeatureTest do
end
end
- test "Plausible.Billing.Feature.Teams.check_availability/2 returns :ok when user is on an enterprise plan" do
- team = new_user() |> subscribe_to_enterprise_plan() |> team_of()
- assert :ok == Plausible.Billing.Feature.Teams.check_availability(team)
- end
-
test "Plausible.Billing.Feature.SharedLinks.check_availability/2 returns :ok when user is on an enterprise plan" do
team = new_user() |> subscribe_to_enterprise_plan() |> team_of()
assert :ok == Plausible.Billing.Feature.SharedLinks.check_availability(team)
diff --git a/test/plausible/billing/quota_test.exs b/test/plausible/billing/quota_test.exs
index 678dc88ac6..f320d89687 100644
--- a/test/plausible/billing/quota_test.exs
+++ b/test/plausible/billing/quota_test.exs
@@ -3,7 +3,7 @@ defmodule Plausible.Billing.QuotaTest do
use Plausible.DataCase, async: true
use Plausible
alias Plausible.Billing.{Quota, Plans}
- alias Plausible.Billing.Feature.{Goals, Props, SitesAPI, StatsAPI, Teams, SharedLinks}
+ alias Plausible.Billing.Feature.{Goals, Props, SitesAPI, StatsAPI, SharedLinks}
use Plausible.Teams.Test
@@ -575,24 +575,18 @@ defmodule Plausible.Billing.QuotaTest do
test "users with expired trials have no access to subscription features" do
team = new_user(trial_expiry_date: ~D[2023-01-01]) |> team_of()
- assert [Goals, Plausible.Billing.Feature.Teams, Plausible.Billing.Feature.SharedLinks] ==
+ assert [Goals, Plausible.Billing.Feature.SharedLinks] ==
Plausible.Teams.Billing.allowed_features_for(team)
end
end
- test "returns all grandfathered features when user is on an old plan" do
- team_on_v1 = new_user() |> subscribe_to_plan(@v1_plan_id) |> team_of()
- team_on_v2 = new_user() |> subscribe_to_plan(@v2_plan_id) |> team_of()
- team_on_v3 = new_user() |> subscribe_to_plan(@v3_plan_id) |> team_of()
+ for {generation, plan_id} <- [{"v1", @v1_plan_id}, {"v2", @v2_plan_id}, {"v3", @v3_plan_id}] do
+ test "returns all grandfathered features when user is on a #{generation} plan" do
+ team = new_user() |> subscribe_to_plan(unquote(plan_id)) |> team_of()
- assert [Goals, Props, StatsAPI, Teams, SharedLinks] ==
- Plausible.Teams.Billing.allowed_features_for(team_on_v1)
-
- assert [Goals, Props, StatsAPI, Teams, SharedLinks] ==
- Plausible.Teams.Billing.allowed_features_for(team_on_v2)
-
- assert [Goals, Props, StatsAPI, Teams, SharedLinks] ==
- Plausible.Teams.Billing.allowed_features_for(team_on_v3)
+ assert [Goals, Props, StatsAPI, SharedLinks] ==
+ Plausible.Teams.Billing.allowed_features_for(team)
+ end
end
test "returns features for a free_10k plan" do
@@ -600,7 +594,7 @@ defmodule Plausible.Billing.QuotaTest do
subscribe_to_plan(user, "free_10k")
team = team_of(user)
- assert [Goals, Props, StatsAPI, Teams, SharedLinks] ==
+ assert [Goals, Props, StatsAPI, SharedLinks] ==
Plausible.Teams.Billing.allowed_features_for(team)
end
@@ -619,7 +613,6 @@ defmodule Plausible.Billing.QuotaTest do
assert [
Plausible.Billing.Feature.StatsAPI,
Plausible.Billing.Feature.Funnels,
- Plausible.Billing.Feature.Teams,
Plausible.Billing.Feature.SharedLinks
] ==
Plausible.Teams.Billing.allowed_features_for(team)
@@ -641,7 +634,7 @@ defmodule Plausible.Billing.QuotaTest do
team = team_of(user)
- assert [Goals, Props, StatsAPI, Teams, SharedLinks] ==
+ assert [Goals, Props, StatsAPI, SharedLinks] ==
Plausible.Teams.Billing.allowed_features_for(team)
end
@@ -669,7 +662,6 @@ defmodule Plausible.Billing.QuotaTest do
assert [
Plausible.Billing.Feature.StatsAPI,
- Plausible.Billing.Feature.Teams,
Plausible.Billing.Feature.SharedLinks
] ==
Plausible.Teams.Billing.allowed_features_for(team)
@@ -687,7 +679,6 @@ defmodule Plausible.Billing.QuotaTest do
assert [
Plausible.Billing.Feature.StatsAPI,
Plausible.Billing.Feature.SitesAPI,
- Plausible.Billing.Feature.Teams,
Plausible.Billing.Feature.SharedLinks
] ==
Plausible.Teams.Billing.allowed_features_for(team)
diff --git a/test/plausible_web/components/billing/billing_test.exs b/test/plausible_web/components/billing/billing_test.exs
index f0f3fd9fc7..fbf7ce481d 100644
--- a/test/plausible_web/components/billing/billing_test.exs
+++ b/test/plausible_web/components/billing/billing_test.exs
@@ -13,7 +13,7 @@ defmodule PlausibleWeb.Components.BillingTest do
%{
current_role: :owner,
current_team: user |> subscribe_to_growth_plan() |> team_of(),
- feature_mod: Plausible.Billing.Feature.Props
+ locked?: true
}
|> render_feature_gate()
@@ -28,7 +28,7 @@ defmodule PlausibleWeb.Components.BillingTest do
%{
current_role: nil,
current_team: nil,
- feature_mod: Plausible.Billing.Feature.Props
+ locked?: true
}
|> render_feature_gate()
@@ -43,7 +43,7 @@ defmodule PlausibleWeb.Components.BillingTest do
%{
current_role: :owner,
current_team: user |> subscribe_to_business_plan() |> team_of(),
- feature_mod: Plausible.Billing.Feature.Funnels
+ locked?: false
}
|> render_feature_gate()
@@ -58,7 +58,7 @@ defmodule PlausibleWeb.Components.BillingTest do
%{
current_role: :owner,
current_team: user |> subscribe_to_growth_plan() |> team_of(),
- feature_mod: Plausible.Billing.Feature.Props
+ locked?: true
}
|> render_feature_gate()
@@ -70,7 +70,7 @@ defmodule PlausibleWeb.Components.BillingTest do
%{
current_role: :billing,
current_team: user |> subscribe_to_growth_plan() |> team_of(),
- feature_mod: Plausible.Billing.Feature.Props
+ locked?: true
}
|> render_feature_gate()
@@ -85,7 +85,7 @@ defmodule PlausibleWeb.Components.BillingTest do
%{
current_role: :editor,
current_team: user |> subscribe_to_growth_plan() |> team_of(),
- feature_mod: Plausible.Billing.Feature.Props
+ locked?: true
}
|> render_feature_gate()
@@ -94,10 +94,11 @@ defmodule PlausibleWeb.Components.BillingTest do
end
defp render_feature_gate(assigns) do
- rendered_to_string(~H"""
+ ~H"""
content...
- """)
+ """
+ |> rendered_to_string()
end
end
diff --git a/test/plausible_web/controllers/admin_controller_test.exs b/test/plausible_web/controllers/admin_controller_test.exs
index 7a4c56d0b9..1f96429689 100644
--- a/test/plausible_web/controllers/admin_controller_test.exs
+++ b/test/plausible_web/controllers/admin_controller_test.exs
@@ -263,7 +263,7 @@ defmodule PlausibleWeb.AdminControllerTest do
conn = get(conn, "/crm/billing/team/#{team.id}/current_plan")
assert json_response(conn, 200) == %{
- "features" => ["goals", "teams", "shared_links"],
+ "features" => ["goals", "shared_links"],
"monthly_pageview_limit" => 10_000_000,
"site_limit" => 10,
"team_member_limit" => 3
diff --git a/test/plausible_web/controllers/settings_controller_test.exs b/test/plausible_web/controllers/settings_controller_test.exs
index da3f6b4955..5451927812 100644
--- a/test/plausible_web/controllers/settings_controller_test.exs
+++ b/test/plausible_web/controllers/settings_controller_test.exs
@@ -1421,16 +1421,6 @@ defmodule PlausibleWeb.SettingsControllerTest do
assert text_of_element(html, ~s/[data-test="create-a-team-cta"]/) == "Create a Team"
end
- test "does not render the 'Create a Team' option if Teams feature is unavailable", %{
- conn: conn,
- user: user
- } do
- subscribe_to_starter_plan(user)
- conn = get(conn, Routes.settings_path(conn, :preferences))
- html = html_response(conn, 200)
- refute element_exists?(html, ~s/[data-test="create-a-team-cta"]/)
- end
-
test "does not render the 'Create a Team' option if a team is already set up", %{
conn: conn,
user: user
diff --git a/test/plausible_web/controllers/site_controller_test.exs b/test/plausible_web/controllers/site_controller_test.exs
index 0e06d7da65..cba58a059b 100644
--- a/test/plausible_web/controllers/site_controller_test.exs
+++ b/test/plausible_web/controllers/site_controller_test.exs
@@ -699,6 +699,7 @@ defmodule PlausibleWeb.SiteControllerTest do
refute resp =~ "Team members automatically have access to this site."
end
+ @tag :ee_only
test "does not render team management notice if Teams feature unavailable", %{
conn: conn,
user: user
diff --git a/test/plausible_web/live/customer_support/teams_test.exs b/test/plausible_web/live/customer_support/teams_test.exs
index 6b78dc5617..660d28ed09 100644
--- a/test/plausible_web/live/customer_support/teams_test.exs
+++ b/test/plausible_web/live/customer_support/teams_test.exs
@@ -128,7 +128,6 @@ defmodule PlausibleWeb.Live.CustomerSupport.TeamsTest do
"false",
"false",
"false",
- "teams",
"false",
"shared_links",
"false",
@@ -150,7 +149,6 @@ defmodule PlausibleWeb.Live.CustomerSupport.TeamsTest do
%Plausible.Billing.EnterprisePlan{
billing_interval: :yearly,
features: [
- Plausible.Billing.Feature.Teams,
Plausible.Billing.Feature.SharedLinks,
Plausible.Billing.Feature.SitesAPI
],
diff --git a/test/plausible_web/live/team_setup_test.exs b/test/plausible_web/live/team_setup_test.exs
index 2725a21ab9..23ca11ef6d 100644
--- a/test/plausible_web/live/team_setup_test.exs
+++ b/test/plausible_web/live/team_setup_test.exs
@@ -83,6 +83,21 @@ defmodule PlausibleWeb.Live.TeamSetupTest do
_ = render(lv)
assert Repo.reload!(team).name == "Team Name 1"
end
+
+ @tag :ee_only
+ test "blurs UI with an upgrade CTA if the subscription team member limit is 0", %{
+ conn: conn,
+ user: user
+ } do
+ subscribe_to_starter_plan(user)
+
+ {:ok, _lv, html} = live(conn, @url)
+
+ assert class_of_element(html, "#feature-gate-inner-block-container") =~
+ "pointer-events-none"
+
+ assert class_of_element(html, "#feature-gate-overlay") =~ "backdrop-blur-[6px]"
+ end
end
describe "/team/setup - full integration" do
diff --git a/test/plausible_web/plugins/api/controllers/capabilities_test.exs b/test/plausible_web/plugins/api/controllers/capabilities_test.exs
index 0affd1a02a..7dbd07c7ea 100644
--- a/test/plausible_web/plugins/api/controllers/capabilities_test.exs
+++ b/test/plausible_web/plugins/api/controllers/capabilities_test.exs
@@ -32,7 +32,6 @@ defmodule PlausibleWeb.Plugins.API.Controllers.CapabilitiesTest do
"StatsAPI" => false,
"SitesAPI" => false,
"SiteSegments" => false,
- "Teams" => false,
"SharedLinks" => false
}
}
@@ -60,7 +59,6 @@ defmodule PlausibleWeb.Plugins.API.Controllers.CapabilitiesTest do
"StatsAPI" => false,
"SitesAPI" => false,
"SiteSegments" => false,
- "Teams" => false,
"SharedLinks" => false
}
}
@@ -90,7 +88,6 @@ defmodule PlausibleWeb.Plugins.API.Controllers.CapabilitiesTest do
"StatsAPI" => true,
"SitesAPI" => false,
"SiteSegments" => true,
- "Teams" => true,
"SharedLinks" => true
}
}
@@ -122,7 +119,6 @@ defmodule PlausibleWeb.Plugins.API.Controllers.CapabilitiesTest do
"StatsAPI" => false,
"SitesAPI" => false,
"SiteSegments" => false,
- "Teams" => true,
"SharedLinks" => true
}
}
@@ -157,7 +153,6 @@ defmodule PlausibleWeb.Plugins.API.Controllers.CapabilitiesTest do
"StatsAPI" => true,
"SitesAPI" => true,
"SiteSegments" => false,
- "Teams" => true,
"SharedLinks" => true
}
}
diff --git a/test/support/dev/controllers/dev_subscription_controller.ex b/test/support/dev/controllers/dev_subscription_controller.ex
index b1b90d0dcc..1cd97d8ca4 100644
--- a/test/support/dev/controllers/dev_subscription_controller.ex
+++ b/test/support/dev/controllers/dev_subscription_controller.ex
@@ -5,6 +5,9 @@ defmodule PlausibleWeb.DevSubscriptionController do
use PlausibleWeb, :controller
alias Plausible.Billing.DevSubscriptions
+ alias Plausible.Auth.User
+ alias Plausible.Teams.Team
+ alias Plausible.Teams
plug PlausibleWeb.RequireAccountPlug
@@ -18,7 +21,7 @@ defmodule PlausibleWeb.DevSubscriptionController do
end
def update_form(conn, _params) do
- team = conn.assigns.current_team |> Plausible.Teams.with_subscription()
+ team = conn.assigns.current_team |> Teams.with_subscription()
if is_nil(team.subscription),
do: raise("Can't render subscription update form without subscription")
@@ -30,20 +33,25 @@ defmodule PlausibleWeb.DevSubscriptionController do
end
def cancel_form(conn, _params) do
- team = conn.assigns.current_team |> Plausible.Teams.with_subscription()
+ team = conn.assigns.current_team |> Teams.with_subscription()
if is_nil(team.subscription),
do: raise("Can't render subscription cancel form without subscription")
render(conn, "cancel_dev_subscription.html",
back_link: Routes.settings_path(conn, :subscription),
- enterprise_plan?: Plausible.Teams.Billing.enterprise_configured?(team)
+ enterprise_plan?: Teams.Billing.enterprise_configured?(team)
)
end
def create(conn, %{"plan_id" => plan_id}) do
- team = conn.assigns.current_team
- DevSubscriptions.create_after_1s(team.id, plan_id)
+ for_team =
+ case conn.assigns do
+ %{current_team: %Team{} = team} -> team
+ %{current_user: %User{} = user} -> Teams.force_create_my_team(user)
+ end
+
+ DevSubscriptions.create_after_1s(for_team.id, plan_id)
redirect(conn, to: Routes.billing_path(PlausibleWeb.Endpoint, :upgrade_success))
end