Calmer multiple teams experience (#5526)
* Offer team switcher on /sites if applicable - in case of empty My Personal Sites view, and with another team with sites being available - redirect straight to first team upon invoking team switcher, if there's only one available - redirect to /sites from team switcher, if there are no set-up teams available * Remove unused test helper * Store and use last team identifier * Remove alert about starting trial when adding first site * Format * Update lib/plausible_web/live/sites.ex Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com> --------- Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com>
This commit is contained in:
parent
24d4ae6a58
commit
ef11425693
|
|
@ -40,6 +40,9 @@ defmodule Plausible.Auth.User do
|
|||
field :totp_token, :string
|
||||
field :totp_last_used_at, :naive_datetime
|
||||
|
||||
# for context perseverance across sessions
|
||||
field :last_team_identifier, Ecto.UUID
|
||||
|
||||
on_ee do
|
||||
# Fields for SSO
|
||||
field :type, Ecto.Enum, values: [:standard, :sso]
|
||||
|
|
|
|||
|
|
@ -41,6 +41,13 @@ defmodule Plausible.Users do
|
|||
:ok
|
||||
end
|
||||
|
||||
@spec remember_last_team(Auth.User.t(), String.t() | nil) :: :ok
|
||||
def remember_last_team(%Auth.User{id: user_id}, team_identifier) do
|
||||
q = from(u in Auth.User, where: u.id == ^user_id)
|
||||
Repo.update_all(q, set: [last_team_identifier: team_identifier])
|
||||
:ok
|
||||
end
|
||||
|
||||
@spec has_email_code?(Auth.User.t()) :: boolean()
|
||||
def has_email_code?(user) do
|
||||
Auth.EmailVerification.any?(user)
|
||||
|
|
|
|||
|
|
@ -93,7 +93,16 @@ defmodule PlausibleWeb.AuthController do
|
|||
}
|
||||
end)
|
||||
|
||||
render(conn, "select_team.html", teams_selection: teams)
|
||||
case teams do
|
||||
[] ->
|
||||
redirect(conn, to: Routes.site_path(conn, :index))
|
||||
|
||||
[%{identifier: sole_team_identifier}] ->
|
||||
redirect(conn, to: Routes.site_path(conn, :index, __team: sole_team_identifier))
|
||||
|
||||
[_ | _] ->
|
||||
render(conn, "select_team.html", teams_selection: teams)
|
||||
end
|
||||
end
|
||||
|
||||
def activate_form(conn, params) do
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ defmodule PlausibleWeb.Live.Sites do
|
|||
:team_invitations,
|
||||
Teams.Invitations.all(socket.assigns.current_user)
|
||||
)
|
||||
|> assign(:filter_text, params["filter_text"] || "")
|
||||
|> assign(:filter_text, String.trim(params["filter_text"] || ""))
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
|
@ -88,13 +88,23 @@ defmodule PlausibleWeb.Live.Sites do
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<p
|
||||
:if={String.trim(@filter_text) != "" and @sites.entries == []}
|
||||
class="mt-4 dark:text-gray-100"
|
||||
>
|
||||
<p :if={@filter_text != "" and @sites.entries == []} class="mt-4 dark:text-gray-100 text-center">
|
||||
No sites found. Please search for something else.
|
||||
</p>
|
||||
|
||||
<p
|
||||
:if={
|
||||
@has_sites? and not Teams.setup?(@current_team) and @sites.entries == [] and
|
||||
@filter_text == ""
|
||||
}
|
||||
class="mt-4 dark:text-gray-100 text-center"
|
||||
>
|
||||
You currently have no personal sites. Are you looking for your team’s sites?
|
||||
<.styled_link href={Routes.auth_path(@socket, :select_team)}>
|
||||
Go to your team →
|
||||
</.styled_link>
|
||||
</p>
|
||||
|
||||
<div :if={@has_sites?}>
|
||||
<ul class="my-6 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<%= for site <- @sites.entries do %>
|
||||
|
|
@ -719,6 +729,7 @@ defmodule PlausibleWeb.Live.Sites do
|
|||
end
|
||||
|
||||
defp set_filter_text(socket, filter_text) do
|
||||
filter_text = String.trim(filter_text)
|
||||
uri = socket.assigns.uri
|
||||
|
||||
uri_params =
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ defmodule PlausibleWeb.AuthPlug do
|
|||
user = user_session.user
|
||||
|
||||
current_team_id_from_session = Plug.Conn.get_session(conn, "current_team_id")
|
||||
current_team_id = conn.params["__team"] || current_team_id_from_session
|
||||
|
||||
current_team_id =
|
||||
conn.params["__team"] || current_team_id_from_session || user.last_team_identifier
|
||||
|
||||
{current_team, current_team_role} =
|
||||
if current_team_id do
|
||||
|
|
@ -35,9 +37,11 @@ defmodule PlausibleWeb.AuthPlug do
|
|||
conn =
|
||||
cond do
|
||||
current_team && current_team_id != current_team_id_from_session ->
|
||||
Plausible.Users.remember_last_team(user, current_team_id)
|
||||
Plug.Conn.put_session(conn, "current_team_id", current_team_id)
|
||||
|
||||
is_nil(current_team) && not is_nil(current_team_id_from_session) ->
|
||||
Plausible.Users.remember_last_team(user, nil)
|
||||
Plug.Conn.delete_session(conn, "current_team_id")
|
||||
|
||||
true ->
|
||||
|
|
|
|||
|
|
@ -14,35 +14,6 @@
|
|||
resource="sites"
|
||||
/>
|
||||
|
||||
<%= if ee?() and (is_nil(@current_team) or is_nil(@current_team.trial_expiry_date)) do %>
|
||||
<div class="rounded-md bg-blue-50 dark:bg-transparent dark:border border-blue-200 p-4">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<svg
|
||||
class="h-5 w-5 text-blue-500 dark:text-blue-300"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<div class="text-sm text-blue-700 dark:text-blue-300">
|
||||
<p>
|
||||
When you create your first site, your account will enter a 30 day free trial.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="my-6">
|
||||
<.input
|
||||
help_text="Just the naked domain or subdomain without 'www', 'https' etc."
|
||||
|
|
|
|||
|
|
@ -1724,6 +1724,48 @@ defmodule PlausibleWeb.AuthControllerTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "GET /team/select" do
|
||||
setup [:create_user, :log_in]
|
||||
|
||||
test "redirects to /sites if no teams available", %{conn: conn} do
|
||||
conn = get(conn, Routes.auth_path(conn, :select_team))
|
||||
assert redirected_to(conn, 302) == Routes.site_path(conn, :index)
|
||||
end
|
||||
|
||||
test "redirects to /sites?__team if one team set up available", %{conn: conn, user: user} do
|
||||
new_site(owner: user)
|
||||
team = team_of(user)
|
||||
assert Plausible.Teams.complete_setup(team)
|
||||
conn = get(conn, Routes.auth_path(conn, :select_team))
|
||||
assert redirected_to(conn, 302) == Routes.site_path(conn, :index, __team: team.identifier)
|
||||
end
|
||||
|
||||
test "displays team switcher if >1 teams available", %{conn: conn, user: user} do
|
||||
t1 = new_site(owner: user).team
|
||||
t2 = new_site().team
|
||||
|
||||
add_member(t2, user: user, role: :viewer)
|
||||
|
||||
Plausible.Teams.complete_setup(t1)
|
||||
Plausible.Teams.complete_setup(t2)
|
||||
|
||||
conn = get(conn, Routes.auth_path(conn, :select_team))
|
||||
assert html = html_response(conn, 200)
|
||||
|
||||
assert text(html) =~ "Switch your current team"
|
||||
|
||||
assert element_exists?(
|
||||
html,
|
||||
~s|a[href="#{Routes.site_path(conn, :index, __team: t1.identifier)}"]|
|
||||
)
|
||||
|
||||
assert element_exists?(
|
||||
html,
|
||||
~s|a[href="#{Routes.site_path(conn, :index, __team: t2.identifier)}"]|
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp login_with_cookie(conn, email, password) do
|
||||
conn
|
||||
|> post(Routes.auth_path(conn, :login), %{
|
||||
|
|
|
|||
|
|
@ -13,9 +13,28 @@ defmodule PlausibleWeb.Live.SitesTest do
|
|||
test "renders empty sites page", %{conn: conn} do
|
||||
{:ok, _lv, html} = live(conn, "/sites")
|
||||
|
||||
assert text(html) =~ "My Personal Sites"
|
||||
text = text(html)
|
||||
assert text =~ "My Personal Sites"
|
||||
assert text =~ "You don't have any sites yet"
|
||||
refute text =~ "You currently have no personal sites"
|
||||
refute text =~ "Go to your team"
|
||||
end
|
||||
|
||||
assert text(html) =~ "You don't have any sites yet"
|
||||
test "renders team switcher link, if on personal sites with other teams available", %{
|
||||
conn: conn,
|
||||
user: user
|
||||
} do
|
||||
team2 = new_site().team
|
||||
|
||||
add_member(team2, user: user, role: :admin)
|
||||
|
||||
{:ok, _lv, html} = live(conn, "/sites")
|
||||
text = text(html)
|
||||
|
||||
assert text =~ "My Personal Sites"
|
||||
refute text =~ "You don't have any sites yet"
|
||||
assert text =~ "You currently have no personal sites"
|
||||
assert text =~ "Go to your team"
|
||||
end
|
||||
|
||||
test "renders settings link when current team is set", %{user: user, conn: conn} do
|
||||
|
|
|
|||
|
|
@ -123,4 +123,41 @@ defmodule PlausibleWeb.AuthPlugTest do
|
|||
assert conn.assigns[:current_team].id == team.id
|
||||
assert conn.assigns[:current_team_role] == :editor
|
||||
end
|
||||
|
||||
test "stores team identifier when team changes", %{conn: conn, user: user} do
|
||||
subscribe_to_plan(user, "123", inserted_at: NaiveDateTime.utc_now())
|
||||
team = team_of(user)
|
||||
|
||||
assert is_nil(user.last_team_identifier)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> Plug.Adapters.Test.Conn.conn(:get, "/", %{__team: team.identifier})
|
||||
|> AuthPlug.call(%{})
|
||||
|
||||
updated_user = Plausible.Repo.reload!(user)
|
||||
assert updated_user.last_team_identifier == team.identifier
|
||||
assert get_session(conn, "current_team_id") == team.identifier
|
||||
end
|
||||
|
||||
test "clears team identifier when recently stored team identifier doesn't exist", %{
|
||||
conn: conn,
|
||||
user: user
|
||||
} do
|
||||
subscribe_to_plan(user, "123", inserted_at: NaiveDateTime.utc_now())
|
||||
|
||||
stale_team_id = Ecto.UUID.generate()
|
||||
:ok = Plausible.Users.remember_last_team(user, stale_team_id)
|
||||
assert Plausible.Repo.reload!(user).last_team_identifier
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_session("current_team_id", stale_team_id)
|
||||
|> Plug.Adapters.Test.Conn.conn(:get, "/", %{})
|
||||
|> AuthPlug.call(%{})
|
||||
|
||||
updated_user = Plausible.Repo.reload!(user)
|
||||
assert is_nil(updated_user.last_team_identifier)
|
||||
refute get_session(conn, "current_team_id")
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -49,12 +49,6 @@ defmodule Plausible.Teams.Test do
|
|||
|> insert(args)
|
||||
end
|
||||
|
||||
def new_team() do
|
||||
new_user()
|
||||
|> Map.fetch!(:team_memberships)
|
||||
|> List.first()
|
||||
end
|
||||
|
||||
def new_user(args \\ []) do
|
||||
{team_args, args} = Keyword.pop(args, :team, [])
|
||||
{trial_expiry_date, args} = Keyword.pop(args, :trial_expiry_date)
|
||||
|
|
|
|||
Loading…
Reference in New Issue