Skip to content

Commit ea6644d

Browse files
aerosolsanne-sanukutaht
committed
Implement consolidated view CTA on /sites
Co-authored-by: Sanne de Vries <[email protected]> Co-authored-by: Uku Taht <[email protected]>
1 parent 1fe9fc2 commit ea6644d

File tree

6 files changed

+354
-49
lines changed

6 files changed

+354
-49
lines changed

assets/js/liveview/live_socket.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import 'phoenix_html'
77
import { Socket } from 'phoenix'
88
import { LiveSocket } from 'phoenix_live_view'
9-
import { Modal } from 'prima'
9+
import { Modal, Dropdown } from 'prima'
1010
import topbar from 'topbar'
1111
/* eslint-enable import/no-unresolved */
1212

@@ -15,7 +15,7 @@ import Alpine from 'alpinejs'
1515
let csrfToken = document.querySelector("meta[name='csrf-token']")
1616
let websocketUrl = document.querySelector("meta[name='websocket-url']")
1717
if (csrfToken && websocketUrl) {
18-
let Hooks = { Modal }
18+
let Hooks = { Modal, Dropdown }
1919
Hooks.Metrics = {
2020
mounted() {
2121
this.handleEvent('send-metrics', ({ event_name }) => {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
defmodule PlausibleWeb.Components.PrimaDropdown do
2+
@moduledoc false
3+
alias Prima.Dropdown
4+
use Phoenix.Component
5+
6+
defdelegate dropdown(assigns), to: Prima.Dropdown
7+
defdelegate dropdown_trigger(assigns), to: Prima.Dropdown
8+
9+
slot(:inner_block, required: true)
10+
11+
# placement: bottom-end should probably be default in prima. Feels more natural
12+
# for dropdown menus than bottom-start which is the current default
13+
def dropdown_menu(assigns) do
14+
~H"""
15+
<Dropdown.dropdown_menu
16+
placement="bottom-end"
17+
class="p-1.5 rounded-md bg-white shadow-xs ring-1 ring-gray-300 focus:outline-none"
18+
>
19+
{render_slot(@inner_block)}
20+
</Dropdown.dropdown_menu>
21+
"""
22+
end
23+
24+
attr(:as, :any, default: nil)
25+
attr(:disabled, :boolean, default: false)
26+
attr(:rest, :global, include: ~w(navigate patch href))
27+
slot(:inner_block, required: true)
28+
29+
def dropdown_item(assigns) do
30+
~H"""
31+
<Dropdown.dropdown_item
32+
as={@as}
33+
disabled={@disabled}
34+
class="rounded-md text-gray-700 data-focus:bg-gray-100 data-focus:text-gray-900 block px-4 py-2 text-sm"
35+
{@rest}
36+
>
37+
{render_slot(@inner_block)}
38+
</Dropdown.dropdown_item>
39+
"""
40+
end
41+
end

lib/plausible_web/live/sites.ex

Lines changed: 166 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,38 @@ defmodule PlausibleWeb.Live.Sites do
8989
<p :if={not @has_sites?} class="dark:text-gray-100">
9090
You don't have any sites yet.
9191
</p>
92-
<div class="mt-4 flex sm:ml-4 sm:mt-0">
93-
<a href={"/sites/new?flow=#{PlausibleWeb.Flows.provisioning()}"} class="button">
94-
+ Add website
95-
</a>
96-
</div>
92+
<!-- The `z-49` class is to make the dropdown appear above the site cards and (TODO) below the top-right drop down. -->
93+
<!-- The proper solution is for Prima to render the dropdown menu within a <.portal> element to avoid -->
94+
<!-- any stacking context issues. TODO -->
95+
<PlausibleWeb.Components.PrimaDropdown.dropdown
96+
:if={@consolidated_view_cta_dismissed?}
97+
class="z-[49]"
98+
id="add-site-dropdown"
99+
>
100+
<PlausibleWeb.Components.PrimaDropdown.dropdown_trigger as={&button/1} mt?={false}>
101+
+ Add <Heroicons.chevron_down mini class="size-4 mt-0.5" />
102+
</PlausibleWeb.Components.PrimaDropdown.dropdown_trigger>
103+
104+
<PlausibleWeb.Components.PrimaDropdown.dropdown_menu>
105+
<PlausibleWeb.Components.PrimaDropdown.dropdown_item
106+
as={&link/1}
107+
href={Routes.site_path(@socket, :new, %{flow: PlausibleWeb.Flows.provisioning()})}
108+
>
109+
+ Add website
110+
</PlausibleWeb.Components.PrimaDropdown.dropdown_item>
111+
<PlausibleWeb.Components.PrimaDropdown.dropdown_item phx-click="consolidated-view-cta-restore">
112+
+ Add consolidated view
113+
</PlausibleWeb.Components.PrimaDropdown.dropdown_item>
114+
</PlausibleWeb.Components.PrimaDropdown.dropdown_menu>
115+
</PlausibleWeb.Components.PrimaDropdown.dropdown>
116+
117+
<a
118+
:if={!@consolidated_view_cta_dismissed?}
119+
href={"/sites/new?flow=#{PlausibleWeb.Flows.provisioning()}"}
120+
class="whitespace-nowrap truncate inline-flex items-center justify-center gap-x-2 font-medium rounded-md px-3.5 py-2.5 text-sm transition-all duration-150 cursor-pointer disabled:cursor-not-allowed bg-indigo-600 text-white hover:bg-indigo-700 focus-visible:outline-indigo-600 disabled:bg-indigo-400/60 disabled:dark:bg-indigo-600/30 disabled:dark:text-white/35"
121+
>
122+
+ Add website
123+
</a>
97124
</div>
98125
99126
<p :if={@filter_text != "" and @sites.entries == []} class="mt-4 dark:text-gray-100 text-center">
@@ -115,7 +142,16 @@ defmodule PlausibleWeb.Live.Sites do
115142
116143
<div :if={@has_sites?}>
117144
<ul class="my-6 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
118-
<!-- Insert upgrade_card here -->
145+
<.consolidated_view_card_cta
146+
:if={
147+
!@consolidated_view and @no_consolidated_view_reason not in [:no_sites, :ce] and
148+
not @consolidated_view_cta_dismissed?
149+
}
150+
can_manage_consolidated_view?={@can_manage_consolidated_view?}
151+
no_consolidated_view_reason={@no_consolidated_view_reason}
152+
current_user={@current_user}
153+
current_team={@current_team}
154+
/>
119155
<.consolidated_view_card
120156
:if={@consolidated_view && consolidated_view_ok_to_display?(@current_team, @current_user)}
121157
can_manage_consolidated_view?={@can_manage_consolidated_view?}
@@ -190,29 +226,84 @@ defmodule PlausibleWeb.Live.Sites do
190226
"""
191227
end
192228

193-
def upgrade_card(assigns) do
229+
def consolidated_view_card_cta(assigns) do
194230
~H"""
195-
<li class="relative col-span-1 flex flex-col justify-between bg-white p-6 dark:bg-gray-800 rounded-md shadow-lg dark:shadow-xl">
231+
<li
232+
data-test-id="consolidated-view-card-cta"
233+
class="relative col-span-1 flex flex-col justify-between bg-white p-6 dark:bg-gray-800 rounded-md shadow-lg dark:shadow-xl"
234+
>
196235
<div class="flex flex-col">
197236
<p class="text-sm text-gray-600 dark:text-gray-400">
198237
Introducing
199238
</p>
200239
<h3 class="text-[1.35rem] font-bold text-gray-900 leading-tighter dark:text-gray-100">
201-
consolidated view
240+
Consolidated view
202241
</h3>
203242
</div>
204-
<p class="text-gray-900 dark:text-gray-100 leading-tighter mb-2.5">
205-
See stats for all your sites in one single dashboard.
206-
</p>
207-
<div class="flex gap-x-2">
208-
<a href="#" class="button">
209-
Upgrade
210-
</a>
211-
<a href="#" class="button button-outline">
212-
Learn more
213-
</a>
243+
244+
<div
245+
:if={@no_consolidated_view_reason == :team_not_setup}
246+
class="flex flex-col gap-y-4"
247+
>
248+
<p class="text-gray-900 dark:text-gray-100 leading-tighter">
249+
To create a consolidated view, you'll need to set up a team.
250+
</p>
251+
<div class="flex gap-x-2">
252+
<.button_link
253+
href={Routes.team_setup_path(PlausibleWeb.Endpoint, :setup)}
254+
mt?={false}
255+
>
256+
Create team
257+
</.button_link>
258+
<.button_link
259+
theme="secondary"
260+
href="https://plausible.io/docs/consolidated-views"
261+
mt?={false}
262+
>
263+
Learn more
264+
</.button_link>
265+
</div>
214266
</div>
215-
<Heroicons.x_mark class="absolute top-6 right-6 size-5 text-gray-400 transition-colors duration-150 cursor-pointer dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" />
267+
268+
<div
269+
:if={@no_consolidated_view_reason == :upgrade_required}
270+
class="flex flex-col gap-y-4"
271+
>
272+
<p
273+
:if={@can_manage_consolidated_view?}
274+
class="text-gray-900 dark:text-gray-100 leading-tighter"
275+
>
276+
Upgrade to the Business plan<span :if={not Teams.setup?(@current_team)}> and set up a team</span> to enable consolidated views.
277+
</p>
278+
279+
<p
280+
:if={not @can_manage_consolidated_view?}
281+
class="text-gray-900 dark:text-gray-100 leading-tighter"
282+
>
283+
Available on Business plans. Contact your team owner to create it.
284+
</p>
285+
286+
<div class="flex gap-x-2">
287+
<.button_link
288+
:if={@can_manage_consolidated_view?}
289+
href={PlausibleWeb.Router.Helpers.billing_url(PlausibleWeb.Endpoint, :choose_plan)}
290+
mt?={false}
291+
>
292+
Upgrade
293+
</.button_link>
294+
295+
<.button_link
296+
theme="secondary"
297+
href="https://plausible.io/docs/consolidated-views"
298+
mt?={false}
299+
>
300+
Learn more
301+
</.button_link>
302+
</div>
303+
</div>
304+
<a phx-click="consolidated-view-cta-dismiss">
305+
<Heroicons.x_mark class="absolute top-6 right-6 size-5 text-gray-400 transition-colors duration-150 cursor-pointer dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" />
306+
</a>
216307
</li>
217308
"""
218309
end
@@ -781,6 +872,28 @@ defmodule PlausibleWeb.Live.Sites do
781872
{:noreply, socket}
782873
end
783874

875+
on_ee do
876+
def handle_event("consolidated-view-cta-dismiss", _, socket) do
877+
:ok =
878+
Plausible.ConsolidatedView.dismiss_cta(
879+
socket.assigns.current_user,
880+
socket.assigns.current_team
881+
)
882+
883+
{:noreply, assign(socket, :consolidated_view_cta_dismissed?, true)}
884+
end
885+
886+
def handle_event("consolidated-view-cta-restore", _, socket) do
887+
:ok =
888+
Plausible.ConsolidatedView.restore_cta(
889+
socket.assigns.current_user,
890+
socket.assigns.current_team
891+
)
892+
893+
{:noreply, assign(socket, :consolidated_view_cta_dismissed?, false)}
894+
end
895+
end
896+
784897
defp load_sites(%{assigns: assigns} = socket) do
785898
sites =
786899
Sites.list_with_invitations(assigns.current_user, assigns.params,
@@ -912,11 +1025,16 @@ defmodule PlausibleWeb.Live.Sites do
9121025
:sha |> :crypto.hash(domain) |> Base.encode16()
9131026
end
9141027

915-
@no_consolidated_view %{
916-
consolidated_view: nil,
917-
can_manage_consolidated_view?: false,
918-
consolidated_stats: nil
919-
}
1028+
def no_consolidated_view(overrides \\ []) do
1029+
[
1030+
consolidated_view: nil,
1031+
can_manage_consolidated_view?: false,
1032+
consolidated_stats: nil,
1033+
no_consolidated_view_reason: nil,
1034+
consolidated_view_cta_dismissed?: false
1035+
]
1036+
|> Keyword.merge(overrides)
1037+
end
9201038

9211039
on_ee do
9221040
alias Plausible.ConsolidatedView
@@ -925,19 +1043,28 @@ defmodule PlausibleWeb.Live.Sites do
9251043
ConsolidatedView.ok_to_display?(team, user)
9261044
end
9271045

928-
defp init_consolidated_view_assigns(_user, nil), do: @no_consolidated_view
1046+
defp init_consolidated_view_assigns(_user, nil) do
1047+
# technically this is team not setup, but is also equivalent of having no sites at this moment (can have invitations though), so CTA should not be shown
1048+
no_consolidated_view(no_consolidated_view_reason: :no_sites)
1049+
end
9291050

9301051
defp init_consolidated_view_assigns(user, team) do
931-
if Teams.setup?(team) do
932-
view = ConsolidatedView.get(team)
1052+
case ConsolidatedView.enable(team) do
1053+
{:ok, view} ->
1054+
%{
1055+
consolidated_view: view,
1056+
can_manage_consolidated_view?: ConsolidatedView.can_manage?(user, team),
1057+
consolidated_stats: :loading,
1058+
no_consolidated_view_reason: nil,
1059+
consolidated_view_cta_dismissed?: ConsolidatedView.cta_dismissed?(user, team)
1060+
}
9331061

934-
%{
935-
consolidated_view: view,
936-
can_manage_consolidated_view?: ConsolidatedView.can_manage?(user, team),
937-
consolidated_stats: :loading
938-
}
939-
else
940-
@no_consolidated_view
1062+
{:error, reason} ->
1063+
no_consolidated_view(
1064+
no_consolidated_view_reason: reason,
1065+
can_manage_consolidated_view?: ConsolidatedView.can_manage?(user, team),
1066+
consolidated_view_cta_dismissed?: ConsolidatedView.cta_dismissed?(user, team)
1067+
)
9411068
end
9421069
end
9431070

@@ -950,7 +1077,10 @@ defmodule PlausibleWeb.Live.Sites do
9501077
end
9511078
else
9521079
defp consolidated_view_ok_to_display?(_team, _user), do: false
953-
defp init_consolidated_view_assigns(_user, _team), do: @no_consolidated_view
1080+
1081+
defp init_consolidated_view_assigns(_user, _team),
1082+
do: no_consolidated_view(no_consolidated_view_reason: :ce)
1083+
9541084
defp load_consolidated_stats(_consolidated_view), do: nil
9551085
end
9561086
end

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ defmodule Plausible.MixProject do
114114
{:phoenix_live_view, "~> 1.1"},
115115
{:php_serializer, "~> 2.0"},
116116
{:plug, "~> 1.13", override: true},
117-
{:prima, "~> 0.1.7"},
117+
{:prima, "~> 0.1.8"},
118118
{:plug_cowboy, "~> 2.3"},
119119
{:polymorphic_embed, "~> 5.0"},
120120
{:postgrex, "~> 0.19.0"},

0 commit comments

Comments
 (0)