Skip to content

Commit

Permalink
Implement Team setup CTA+view (#4960)
Browse files Browse the repository at this point in the history
* spike

* wip omg

* Add option to clear combobox dropdown on selection

* Extend combobox on selection made callback

* Remove IO.inspect

* Add more guests via seeds

* Implement basic UX for arbitrary e-mail addresses

* Save team name on change

* Don't crash on invalid team name input

* Enable :teams flag via seeds

* Implement changing roles

* Display avatars in combobox when selecting member candidates

* Reduce noise

* Add 'Create team' button to the layout

* Revert "Extend combobox on selection made callback"

This reverts commit 874d566.

* Fix seeds

* Make email submittion optional in InviteToTeam service

* Implement finalising team setup (WIP)

* Revert "Display avatars in combobox when selecting member candidates"

This reverts commit ff1a30d.

* Use regular redirect to preserve flash

* Check team's setup complete status on mount

* Add `dev` param, allowing redirect skip on setup complete

* Bootstrap test module

* Add sample test touching on combobox in team setup

* WIP: testing team setup LV

* Fixup test

* Remove unused bindings

* Add another minor test

* Test removing a member candidate

* Finish up remaining tests

* Rename main module

* Put  team setup view behind a feature flag

* Update main nav dropdown

* Update ComboBox tests

* Fix CandidatesTest regression

* Bring back OG combo-box.js

* Fix dark mode red

* Minor styling fixes

* Extract setup_team function into context

* Remove Floki calls, use Plausible.HTML proxy instead

* Make credo happy

* Use path helpers instead of hardcoded paths

* Fix formatting

* Revert "Merge branch 'master' into setup-teams-01"

This reverts commit bc436c5, reversing
changes made to 2eb128d.

* Reapply "Merge branch 'master' into setup-teams-01"

This reverts commit c7ebdd2.

* Alter clear_on_select behavior

* Revert "Alter clear_on_select behavior"

This reverts commit deb20c4.

* Look up guests with distinct:true

* Revert "Look up guests with distinct:true"

This reverts commit 352d271.

* Look up guests with distinct:true

* Try something dumb

* huh?

* Bring back problematic changes

* Make combobox dropdown open state and spinner behave on form update

* Don't explicitly send update on live component attribute update

* Don't explicitly send updates to options in funnel combobox either

* Revert "Temporarily disable combobox spinner (#4971)"

This reverts commit 3abf974.

* Fix formatting

* Set 'id' on element with phx-update=ignore

* Rework options and suggestions setup in combobox

* Fix suggestions for async

* Fix formatting

---------

Co-authored-by: Adrian Gruntkowski <[email protected]>
  • Loading branch information
aerosol and zoldar authored Jan 15, 2025
1 parent 5427a7a commit 558352c
Show file tree
Hide file tree
Showing 17 changed files with 824 additions and 32 deletions.
9 changes: 3 additions & 6 deletions extra/lib/plausible_web/live/funnel_settings/form.ex
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ defmodule PlausibleWeb.Live.FunnelSettings.Form do
end
}
id={"step-#{step_idx}"}
options={reject_already_selected("step-#{step_idx}", @goals, @selections_made)}
options={reject_already_selected(@goals, @selections_made)}
/>
</div>
Expand Down Expand Up @@ -385,16 +385,13 @@ defmodule PlausibleWeb.Live.FunnelSettings.Form do
Map.delete(selections_made, step_input_id)
end

defp reject_already_selected(combo_box, goals, selections_made) do
defp reject_already_selected(goals, selections_made) do
selection_ids =
Enum.map(selections_made, fn
{_, %{id: goal_id}} -> goal_id
end)

result = Enum.reject(goals, fn {goal_id, _} -> goal_id in selection_ids end)

send_update(PlausibleWeb.Live.Components.ComboBox, id: combo_box, suggestions: result)
result
Enum.reject(goals, fn {goal_id, _} -> goal_id in selection_ids end)
end

defp find_preselected(%Funnel{} = funnel, false, idx) do
Expand Down
33 changes: 33 additions & 0 deletions lib/plausible/teams.ex
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,39 @@ defmodule Plausible.Teams do
)
end

def setup_team(team, candidates) do
inviter = Repo.preload(team, :owner).owner

setup_team_fn = fn {{email, _name}, role} ->
case Teams.Invitations.InviteToTeam.invite(team, inviter, email, role, send_email?: false) do
{:ok, invitation} -> invitation
{:error, error} -> Repo.rollback(error)
end
end

result =
Repo.transaction(fn ->
team
|> Teams.Team.setup_changeset()
|> Repo.update!()

Enum.map(candidates, setup_team_fn)
end)

case result do
{:ok, invitations} ->
Enum.each(invitations, fn invitation ->
invitee = Plausible.Auth.find_user_by(email: invitation.email)
Teams.Invitations.InviteToTeam.send_invitation_email(invitation, invitee)
end)

{:ok, invitations}

{:error, {:over_limit, _}} = error ->
error
end
end

defp create_my_team(user) do
team =
"My Team"
Expand Down
16 changes: 15 additions & 1 deletion lib/plausible/teams/invitations/candidates.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,25 @@ defmodule Plausible.Teams.Invitations.Candidates do
inner_join: u in assoc(tm, :user),
where: gm.site_id in ^all_site_ids,
where: ilike(u.email, ^term) or ilike(u.name, ^term),
where: u.id not in ^for(u <- exclude, do: u.id),
where: u.email not in ^exclude,
order_by: [asc: u.id],
select: u,
distinct: true,
limit: ^limit
)
end

def get_site_guest(%Teams.Team{} = team, email) do
all_site_ids = Teams.owned_sites_ids(team)

Repo.one(
from gm in GuestMembership,
inner_join: tm in assoc(gm, :team_membership),
inner_join: u in assoc(tm, :user),
where: gm.site_id in ^all_site_ids,
where: u.email == ^email,
distinct: true,
select: u
)
end
end
6 changes: 4 additions & 2 deletions lib/plausible/teams/invitations/invite_to_team.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ defmodule Plausible.Teams.Invitations.InviteToTeam do
),
{:ok, invitation} <-
Teams.Invitations.invite(team, invitee_email, role, inviter) do
send_invitation_email(invitation, invitee)
if Keyword.get(opts, :send_email?, true) do
send_invitation_email(invitation, invitee)
end

{:ok, invitation}
end
Expand All @@ -45,7 +47,7 @@ defmodule Plausible.Teams.Invitations.InviteToTeam do
raise "Invalid role passed: #{inspect(role)}"
end

defp send_invitation_email(invitation, invitee) do
def send_invitation_email(invitation, invitee) do
invitation
|> Repo.preload([:team, :inviter])
|> Teams.Invitations.send_invitation_email(invitee)
Expand Down
10 changes: 10 additions & 0 deletions lib/plausible/teams/team.ex
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ defmodule Plausible.Teams.Team do
|> validate_required(:name)
end

def setup_changeset(team) do
now = NaiveDateTime.utc_now(:second)

team
|> change(
setup_complete: true,
setup_at: now
)
end

def start_trial(team, today \\ Date.utc_today()) do
trial_expiry = trial_expiry(today)

Expand Down
52 changes: 34 additions & 18 deletions lib/plausible_web/live/components/combo_box.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
if connected?(socket) do
socket
|> assign_options()
|> assign_suggestions()
else
socket
end
|> assign_suggestions(assigns[:suggestions])

{:ok, socket}
end
Expand All @@ -68,18 +68,15 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
attr(:suggest_fun, :any, required: true)
attr(:suggestions_limit, :integer)
attr(:class, :string, default: "")
attr(:clear_on_select, :boolean, default: false)
attr(:required, :boolean, default: false)
attr(:creatable, :boolean, default: false)
attr(:creatable_prompt, :string, default: "Create")
attr(:errors, :list, default: [])
attr(:async, :boolean, default: Mix.env() != :test)
attr(:on_selection_made, :any)

def render(assigns) do
assigns =
assign_new(assigns, :suggestions, fn ->
Enum.take(assigns.options, suggestions_limit(assigns))
end)

~H"""
<div
id={"input-picker-main-#{@id}"}
Expand Down Expand Up @@ -115,7 +112,12 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
/>
<.spinner class="spinner hidden absolute inset-y-3 right-8" />
<.spinner x-show="selectionInProgress" class="spinner absolute inset-y-3 right-8" />
<.spinner
id={"selection-in-progress-#{@id}"}
phx-update="ignore"
x-show="selectionInProgress"
class="spinner absolute inset-y-3 right-8"
/>
<.dropdown_anchor id={@id} />
Expand All @@ -136,6 +138,7 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
target={@myself}
creatable={@creatable}
display_value={@display_value}
creatable_prompt={@creatable_prompt}
/>
</div>
</div>
Expand Down Expand Up @@ -170,6 +173,7 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
attr(:suggest_fun, :any, required: true)
attr(:target, :any)
attr(:creatable, :boolean, required: true)
attr(:creatable_prompt, :string, default: nil)
attr(:display_value, :string, required: true)

def combo_dropdown(assigns) do
Expand All @@ -180,6 +184,7 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
x-show="isOpen"
x-ref="suggestions"
class="text-sm w-full dropdown z-50 absolute mt-1 max-h-60 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-900"
style="display: none;"
>
<.option
:if={display_creatable_option?(assigns)}
Expand All @@ -189,6 +194,7 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
target={@target}
ref={@ref}
creatable
creatable_prompt={@creatable_prompt}
/>
<.option
Expand Down Expand Up @@ -229,6 +235,7 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
attr(:target, :any)
attr(:idx, :integer, required: true)
attr(:creatable, :boolean, default: false)
attr(:creatable_prompt, :string, required: false)

def option(assigns) do
assigns = assign(assigns, :suggestions_limit, suggestions_limit(assigns))
Expand All @@ -245,15 +252,15 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
>
<a
x-ref={"dropdown-#{@ref}-option-#{@idx}"}
x-on:click={not @creatable && "selectionInProgress = false"}
x-on:click={not @creatable && "selectionInProgress = true"}
phx-click={select_option(@ref, @submit_value, @display_value)}
phx-value-submit-value={@submit_value}
phx-value-display-value={@display_value}
phx-target={@target}
class="block truncate py-2 px-3"
>
<%= if @creatable do %>
Create "{@display_value}"
{@creatable_prompt} "{@display_value}"
<% else %>
{@display_value}
<% end %>
Expand Down Expand Up @@ -317,6 +324,13 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
defp do_select(socket, submit_value, display_value) do
id = socket.assigns.id

display_value =
if socket.assigns[:clear_on_select] do
""
else
display_value
end

socket =
socket
|> push_event("update-value", %{id: id, value: display_value, fire: false})
Expand Down Expand Up @@ -350,15 +364,17 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
end)
end

defp assign_suggestions(socket) do
if socket.assigns[:suggestions] do
assign(
socket,
suggestions: Enum.take(socket.assigns.suggestions, suggestions_limit(socket.assigns))
)
else
socket
end
defp assign_suggestions(socket, nil = _suggestions_from_update) do
suggestions =
socket.assigns
|> Map.get(:options, [])
|> Enum.take(suggestions_limit(socket.assigns))

assign(socket, suggestions: suggestions)
end

defp assign_suggestions(socket, _suggestions_from_update) do
socket
end

defp select_default(socket) do
Expand Down
2 changes: 1 addition & 1 deletion lib/plausible_web/live/components/form.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ defmodule PlausibleWeb.Live.Components.Form do
<.input name="my-input" errors={["oh no!"]} />
"""

@default_input_class "text-sm text-gray-900 dark:text-white dark:bg-gray-900 block pl-3.5 py-2.5 border-gray-300 dark:border-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 rounded-md"
@default_input_class "text-sm text-gray-900 dark:text-white dark:bg-gray-900 block pl-3.5 py-2 border-gray-300 dark:border-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 rounded-md"

attr(:id, :any, default: nil)
attr(:name, :any)
Expand Down
Loading

0 comments on commit 558352c

Please sign in to comment.