Skip to content

Commit a2ec1f0

Browse files
committed
Use ReportList component in LV pages breakdown
1 parent 08da688 commit a2ec1f0

File tree

2 files changed

+193
-13
lines changed

2 files changed

+193
-13
lines changed

lib/plausible_web/live/dashboard.ex

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,15 @@ defmodule PlausibleWeb.Live.Dashboard do
66
use PlausibleWeb, :live_view
77

88
alias Plausible.Repo
9+
alias Plausible.Stats.DashboardQueryParser
10+
alias Plausible.Stats.QueryBuilder
911
alias Plausible.Teams
1012

13+
@default_prefs %{
14+
"period" => "28d",
15+
"match_day_of_week" => true
16+
}
17+
1118
@spec enabled?(Plausible.Site.t() | nil) :: boolean()
1219
def enabled?(nil), do: false
1320

@@ -16,7 +23,8 @@ defmodule PlausibleWeb.Live.Dashboard do
1623
end
1724

1825
def mount(_params, %{"domain" => domain, "url" => url}, socket) do
19-
user_prefs = get_connect_params(socket)["user_prefs"] || %{}
26+
# TODO: make it more permissive of invalid values in search params and stored values
27+
user_prefs = Map.merge(@default_prefs, get_connect_params(socket)["user_prefs"] || %{})
2028

2129
# As domain is passed via session, the associated site has already passed
2230
# validation logic on plug level.
@@ -34,14 +42,25 @@ defmodule PlausibleWeb.Live.Dashboard do
3442
|> assign(:connected?, connected?(socket))
3543
|> assign(:site, site)
3644
|> assign(:user_prefs, user_prefs)
37-
|> assign(:params, %{})
3845

3946
{:noreply, socket} = handle_params_internal(%{}, url, socket)
4047

4148
{:ok, socket}
4249
end
4350

44-
def handle_params_internal(_params, _url, socket) do
51+
def handle_params_internal(_params, url, socket) do
52+
uri = URI.new!(url)
53+
path = uri.path |> String.split("/") |> Enum.drop(2)
54+
{:ok, params} = DashboardQueryParser.parse(uri.query || "", socket.assigns.user_prefs)
55+
{:ok, query} = QueryBuilder.build(socket.assigns.site, params, %{})
56+
57+
socket =
58+
assign(socket,
59+
path: path,
60+
params: params,
61+
query: query
62+
)
63+
4564
{:noreply, socket}
4665
end
4766

@@ -55,6 +74,8 @@ defmodule PlausibleWeb.Live.Dashboard do
5574
site={@site}
5675
user_prefs={@user_prefs}
5776
connected?={@connected?}
77+
params={@params}
78+
query={@query}
5879
/>
5980
</.portal_wrapper>
6081
</div>

lib/plausible_web/live/dashboard/pages.ex

Lines changed: 169 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,105 @@ defmodule PlausibleWeb.Live.Dashboard.Pages do
55

66
use PlausibleWeb, :live_component
77

8-
alias PlausibleWeb.Components.Dashboard.Base
8+
alias PlausibleWeb.Components.Dashboard.ReportList
99
alias PlausibleWeb.Components.Dashboard.Tile
1010

11+
alias Plausible.Stats
12+
alias Plausible.Stats.Filters
13+
1114
@tabs [
1215
{"pages", "Top Pages"},
1316
{"entry-pages", "Entry Pages"},
1417
{"exit-pages", "Exit Pages"}
1518
]
1619

17-
@tab_labels Map.new(@tabs)
20+
@key_labels %{
21+
"pages" => "Page",
22+
"entry-pages" => "Entry page",
23+
"exit-pages" => "Exit page"
24+
}
25+
26+
@max_items 9
27+
@pagination_params {@max_items, 1}
28+
29+
@metrics %{
30+
"pages" => %{
31+
visitors: %{
32+
width: "w-24",
33+
key: :visitors,
34+
label: "Visitors",
35+
sortable: true,
36+
plot: true
37+
},
38+
conversion_rate: %{
39+
width: "w-24",
40+
key: :conversion_rate,
41+
label: "CR",
42+
sortable: true
43+
}
44+
},
45+
"entry-pages" => %{
46+
visitors: %{
47+
width: "w-24",
48+
key: :visitors,
49+
label: "Unique Entrances",
50+
sortable: true,
51+
plot: true
52+
},
53+
conversion_rate: %{
54+
width: "w-24",
55+
key: :conversion_rate,
56+
label: "CR",
57+
sortable: true
58+
}
59+
},
60+
"exit-pages" => %{
61+
visitors: %{
62+
width: "w-24",
63+
key: :visitors,
64+
label: "Unique Exits",
65+
sortable: true,
66+
plot: true
67+
},
68+
conversion_rate: %{
69+
width: "w-24",
70+
key: :conversion_rate,
71+
label: "CR",
72+
sortable: true
73+
}
74+
}
75+
}
76+
77+
@filter_dimensions %{
78+
"pages" => "event:page",
79+
"entry-pages" => "visit:entry_page",
80+
"exit-pages" => "visit:exit_page"
81+
}
1882

1983
def update(assigns, socket) do
2084
active_tab = assigns.user_prefs["pages_tab"] || "pages"
2185

2286
socket =
2387
assign(socket,
2488
site: assigns.site,
89+
params: assigns.params,
90+
query: assigns.query,
2591
tabs: @tabs,
26-
tab_labels: @tab_labels,
92+
key_labels: @key_labels,
93+
filter_dimensions: @filter_dimensions,
2794
active_tab: active_tab,
2895
connected?: assigns.connected?
2996
)
97+
|> load_metrics()
3098

3199
{:ok, socket}
32100
end
33101

34102
def render(assigns) do
103+
assigns = assign(assigns, :external_link_fn, &external_link/1)
35104
~H"""
36105
<div>
37-
<Tile.tile id="breakdown-tile-pages" title={@tab_labels[@active_tab]} connected?={@connected?}>
106+
<Tile.tile id="breakdown-tile-pages" title={@key_labels[@active_tab]} connected?={@connected?}>
38107
<:tabs>
39108
<Tile.tab
40109
:for={{value, label} <- @tabs}
@@ -45,23 +114,113 @@ defmodule PlausibleWeb.Live.Dashboard.Pages do
45114
/>
46115
</:tabs>
47116
48-
<div class="mx-auto font-medium text-gray-500 dark:text-gray-400">
49-
<Base.dashboard_link site={@site} href="?f=is,source,Direct / None">
50-
Filter by source Direct / None
51-
</Base.dashboard_link>
52-
</div>
117+
<ReportList.report
118+
site={@site}
119+
key_label={@key_labels[@active_tab]}
120+
filter_dimension={@filter_dimensions[@active_tab]}
121+
params={@params}
122+
results={@results}
123+
meta={@meta}
124+
metrics={@metrics}
125+
external_link_fn={@external_link_fn}
126+
/>
53127
</Tile.tile>
54128
</div>
55129
"""
56130
end
57131

58132
def handle_event("set-tab", %{"tab" => tab}, socket) do
59133
if tab != socket.assigns.active_tab do
60-
socket = assign(socket, :active_tab, tab)
134+
socket =
135+
socket
136+
|> assign(:active_tab, tab)
137+
|> load_metrics()
61138

62139
{:noreply, socket}
63140
else
64141
{:noreply, socket}
65142
end
66143
end
144+
145+
defp external_link(_item) do
146+
"https://example.com"
147+
end
148+
149+
defp load_metrics(socket) do
150+
%{results: pages, meta: meta, metrics: metrics} =
151+
metrics_for_tab(socket.assigns.active_tab, socket.assigns.site, socket.assigns.query)
152+
153+
assign(
154+
socket,
155+
metrics: Enum.map(metrics, &Map.fetch!(@metrics[socket.assigns.active_tab], &1)),
156+
results: Enum.take(pages, @max_items),
157+
meta: Map.merge(meta, Stats.Breakdown.formatted_date_ranges(socket.assigns.query)),
158+
skip_imported_reason: meta[:imports_skip_reason]
159+
)
160+
end
161+
162+
defp metrics_for_tab("pages", site, query) do
163+
query = struct!(query, dimensions: ["event:page"])
164+
165+
metrics = breakdown_metrics(query)
166+
167+
%{results: results, meta: meta} = Stats.breakdown(site, query, metrics, @pagination_params)
168+
169+
pages =
170+
results
171+
|> transform_keys(%{page: :name})
172+
173+
%{results: pages, meta: meta, metrics: metrics}
174+
end
175+
176+
defp metrics_for_tab("entry-pages", site, query) do
177+
query = struct!(query, dimensions: ["visit:entry_page"])
178+
179+
metrics = breakdown_metrics(query)
180+
181+
%{results: results, meta: meta} = Stats.breakdown(site, query, metrics, @pagination_params)
182+
183+
pages =
184+
results
185+
|> transform_keys(%{entry_page: :name})
186+
187+
%{results: pages, meta: meta, metrics: metrics}
188+
end
189+
190+
defp metrics_for_tab("exit-pages", site, query) do
191+
query = struct!(query, dimensions: ["visit:exit_page"])
192+
193+
metrics = breakdown_metrics(query)
194+
195+
%{results: results, meta: meta} = Stats.breakdown(site, query, metrics, @pagination_params)
196+
197+
pages =
198+
results
199+
|> transform_keys(%{exit_page: :name})
200+
201+
%{results: pages, meta: meta, metrics: metrics}
202+
end
203+
204+
defp breakdown_metrics(query) do
205+
if toplevel_goal_filter?(query) do
206+
[:visitors, :conversion_rate]
207+
else
208+
[:visitors]
209+
end
210+
end
211+
212+
defp transform_keys(result, keys_to_replace) when is_map(result) do
213+
for {key, val} <- result, do: {Map.get(keys_to_replace, key, key), val}, into: %{}
214+
end
215+
216+
defp transform_keys(results, keys_to_replace) when is_list(results) do
217+
Enum.map(results, &transform_keys(&1, keys_to_replace))
218+
end
219+
220+
defp toplevel_goal_filter?(query) do
221+
Filters.filtering_on_dimension?(query, "event:goal",
222+
max_depth: 0,
223+
behavioral_filters: :ignore
224+
)
225+
end
67226
end

0 commit comments

Comments
 (0)