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