diff --git a/assets/js/dashboard/stats/modals/countries.js b/assets/js/dashboard/stats/modals/countries.js
index ff5cb9e8a..e04b68e08 100644
--- a/assets/js/dashboard/stats/modals/countries.js
+++ b/assets/js/dashboard/stats/modals/countries.js
@@ -22,7 +22,15 @@ class CountriesModal extends React.Component {
}
label() {
- return this.state.query.period === 'realtime' ? 'Current visitors' : 'Visitors'
+ if (this.state.query.period === 'realtime') {
+ return 'Current visitors'
+ }
+
+ if (this.showConversionRate()) {
+ return 'Conversions'
+ }
+
+ return 'Visitors'
}
showConversionRate() {
@@ -48,8 +56,9 @@ class CountriesModal extends React.Component {
{countryFullName}
+ {this.showConversionRate() &&
{country.total_visitors} | }
- {numberFormatter(country.count)} ({country.percentage}%)
+ {numberFormatter(country.count)} {!this.showConversionRate() && ({country.percentage}%)}
|
{this.showConversionRate() && {country.conversion_rate}% | }
@@ -79,6 +88,7 @@ class CountriesModal extends React.Component {
>
Country
+ {this.showConversionRate() && Total visitors | }
+ {this.showConversionRate() && | {numberFormatter(page.total_visitors)} | }
{numberFormatter(page.count)} |
- {numberFormatter(page.entries)} |
- {this.showVisitDuration() && {durationFormatter(page.visit_duration)} | }
+ {this.showExtra() && {numberFormatter(page.entries)} | }
+ {this.showExtra() && {durationFormatter(page.visit_duration)} | }
{this.showConversionRate() && {numberFormatter(page.conversion_rate)}% | }
)
@@ -115,26 +128,11 @@ class EntryPagesModal extends React.Component {
align="left"
>Page url
- Unique Entrances
- |
- Total Entrances
- |
- Visit Duration
- |
- {this.showConversionRate() && CR
- | }
+ {this.showConversionRate() && Total Visitors | }
+ {this.label()} |
+ {this.showExtra() && Total Entrances | }
+ {this.showExtra() && Visit Duration | }
+ {this.showConversionRate() && CR | }
diff --git a/assets/js/dashboard/stats/modals/exit-pages.js b/assets/js/dashboard/stats/modals/exit-pages.js
index f88c24008..9dff30751 100644
--- a/assets/js/dashboard/stats/modals/exit-pages.js
+++ b/assets/js/dashboard/stats/modals/exit-pages.js
@@ -46,6 +46,22 @@ class ExitPagesModal extends React.Component {
return !!this.state.query.filters.goal
}
+ showExtra() {
+ return this.state.query.period !== 'realtime' && !this.showConversionRate()
+ }
+
+ label() {
+ if (this.state.query.period === 'realtime') {
+ return 'Current visitors'
+ }
+
+ if (this.showConversionRate()) {
+ return 'Conversions'
+ }
+
+ return 'Visitors'
+ }
+
renderPage(page) {
const query = new URLSearchParams(window.location.search)
query.set('exit_page', page.name)
@@ -55,9 +71,10 @@ class ExitPagesModal extends React.Component {
{page.name}
|
+ {this.showConversionRate() && {numberFormatter(page.total_visitors)} | }
{numberFormatter(page.count)} |
- {numberFormatter(page.exits)} |
- {this.formatPercentage(page.exit_rate)} |
+ {this.showExtra() && {numberFormatter(page.exits)} | }
+ {this.showExtra() && {this.formatPercentage(page.exit_rate)} | }
{this.showConversionRate() && {numberFormatter(page.conversion_rate)}% | }
)
@@ -89,9 +106,10 @@ class ExitPagesModal extends React.Component {
Page url |
- Unique Exits |
- Total Exits |
- Exit Rate |
+ {this.showConversionRate() && Total Visitors | }
+ {this.label()} |
+ {this.showExtra() && Total Exits | }
+ {this.showExtra() && Exit Rate | }
{this.showConversionRate() && CR | }
diff --git a/assets/js/dashboard/stats/modals/pages.js b/assets/js/dashboard/stats/modals/pages.js
index df61b5998..17e945121 100644
--- a/assets/js/dashboard/stats/modals/pages.js
+++ b/assets/js/dashboard/stats/modals/pages.js
@@ -66,6 +66,7 @@ class PagesModal extends React.Component {
{page.name}
|
+ {this.showConversionRate() && {page.total_visitors} | }
{numberFormatter(page.count)} |
{this.showPageviews() && {numberFormatter(page.pageviews)} | }
{this.showExtra() && {this.formatBounceRate(page)} | }
@@ -76,7 +77,15 @@ class PagesModal extends React.Component {
}
label() {
- return this.state.query.period === 'realtime' ? 'Current visitors' : 'Visitors'
+ if (this.state.query.period === 'realtime') {
+ return 'Current visitors'
+ }
+
+ if (this.showConversionRate()) {
+ return 'Conversions'
+ }
+
+ return 'Visitors'
}
renderLoading() {
@@ -105,6 +114,7 @@ class PagesModal extends React.Component {
Page url |
+ {this.showConversionRate() && Total visitors | }
{ this.label() } |
{this.showPageviews() && Pageviews | }
{this.showExtra() && Bounce rate | }
diff --git a/assets/js/dashboard/stats/modals/referrer-drilldown.js b/assets/js/dashboard/stats/modals/referrer-drilldown.js
index 20e62f660..aff3c236d 100644
--- a/assets/js/dashboard/stats/modals/referrer-drilldown.js
+++ b/assets/js/dashboard/stats/modals/referrer-drilldown.js
@@ -27,6 +27,22 @@ class ReferrerDrilldownModal extends React.Component {
return this.state.query.period !== 'realtime' && !this.state.query.filters.goal
}
+ showConversionRate() {
+ return !!this.state.query.filters.goal
+ }
+
+ label() {
+ if (this.state.query.period === 'realtime') {
+ return 'Current visitors'
+ }
+
+ if (this.showConversionRate()) {
+ return 'Conversions'
+ }
+
+ return 'Visitors'
+ }
+
formatBounceRate(ref) {
if (typeof(ref.bounce_rate) === 'number') {
return ref.bounce_rate + '%'
@@ -107,9 +123,11 @@ class ReferrerDrilldownModal extends React.Component {
{ referrer.tweets.map(this.renderTweet) }
+ {this.showConversionRate() && {numberFormatter(referrer.total_visitors)} | }
{numberFormatter(referrer.count)} |
{this.showExtra() && {this.formatBounceRate(referrer)} | }
{this.showExtra() && {this.formatDuration(referrer)} | }
+ {this.showConversionRate() && {referrer.conversion_rate}% | }
)
} else {
@@ -118,9 +136,11 @@ class ReferrerDrilldownModal extends React.Component {
{ this.renderReferrerName(referrer) }
|
+ {this.showConversionRate() && {numberFormatter(referrer.total_visitors)} | }
{numberFormatter(referrer.count)} |
{this.showExtra() && {this.formatBounceRate(referrer)} | }
{this.showExtra() && {this.formatDuration(referrer)} | }
+ {this.showConversionRate() && {referrer.conversion_rate}% | }
)
}
@@ -153,9 +173,11 @@ class ReferrerDrilldownModal extends React.Component {
Referrer |
- Visitors |
+ {this.showConversionRate() && Total visitors | }
+ {this.label()} |
{this.showExtra() && Bounce rate | }
{this.showExtra() && Visit duration | }
+ {this.showConversionRate() && CR | }
diff --git a/assets/js/dashboard/stats/modals/sources.js b/assets/js/dashboard/stats/modals/sources.js
index 308e349a5..944d1f331 100644
--- a/assets/js/dashboard/stats/modals/sources.js
+++ b/assets/js/dashboard/stats/modals/sources.js
@@ -97,6 +97,7 @@ class SourcesModal extends React.Component {
/>
{ source.name }
+ {this.showConversionRate() && {numberFormatter(source.total_visitors)} | }
{numberFormatter(source.count)} |
{this.showExtra() && {this.formatBounceRate(source)} | }
{this.showExtra() && {this.formatDuration(source)} | }
@@ -106,7 +107,15 @@ class SourcesModal extends React.Component {
}
label() {
- return this.state.query.period === 'realtime' ? 'Current visitors' : 'Visitors'
+ if (this.state.query.period === 'realtime') {
+ return 'Current visitors'
+ }
+
+ if (this.showConversionRate()) {
+ return 'Conversions'
+ }
+
+ return 'Visitors'
}
renderLoading() {
@@ -139,6 +148,7 @@ class SourcesModal extends React.Component {
Source |
+ {this.showConversionRate() && Total visitors | }
{this.label()} |
{this.showExtra() && Bounce rate | }
{this.showExtra() && Visit duration | }
diff --git a/assets/js/dashboard/stats/pages/entry-pages.js b/assets/js/dashboard/stats/pages/entry-pages.js
index 5f26edf94..13414c29c 100644
--- a/assets/js/dashboard/stats/pages/entry-pages.js
+++ b/assets/js/dashboard/stats/pages/entry-pages.js
@@ -38,6 +38,18 @@ export default class EntryPages extends React.Component {
.then((res) => this.setState({loading: false, pages: res}))
}
+ label() {
+ if (this.props.query.period === 'realtime') {
+ return 'Current visitors'
+ }
+
+ if (this.showConversionRate()) {
+ return 'Conversions'
+ }
+
+ return 'Unique Entrances'
+ }
+
renderPage(page) {
const externalLink = url.externalLinkForPage(this.props.site.domain, page.name)
const maxWidthDeduction = this.showConversionRate() ? "10rem" : "5rem"
@@ -80,7 +92,7 @@ export default class EntryPages extends React.Component {
Page url
- Unique Entrances
+ {this.label()}
{this.showConversionRate() && CR}
diff --git a/assets/js/dashboard/stats/pages/exit-pages.js b/assets/js/dashboard/stats/pages/exit-pages.js
index f93434cbf..ba8970a59 100644
--- a/assets/js/dashboard/stats/pages/exit-pages.js
+++ b/assets/js/dashboard/stats/pages/exit-pages.js
@@ -33,6 +33,18 @@ export default class ExitPages extends React.Component {
return !!this.props.query.filters.goal
}
+ label() {
+ if (this.props.query.period === 'realtime') {
+ return 'Current visitors'
+ }
+
+ if (this.showConversionRate()) {
+ return 'Conversions'
+ }
+
+ return 'Unique Exits'
+ }
+
fetchPages() {
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/exit-pages`, this.props.query)
.then((res) => this.setState({loading: false, pages: res}))
@@ -80,7 +92,7 @@ export default class ExitPages extends React.Component {
Page url
- Unique Exits
+ {this.label()}
{this.showConversionRate() && CR}
diff --git a/assets/js/dashboard/stats/pages/pages.js b/assets/js/dashboard/stats/pages/pages.js
index c83913fac..d9c153516 100644
--- a/assets/js/dashboard/stats/pages/pages.js
+++ b/assets/js/dashboard/stats/pages/pages.js
@@ -43,6 +43,10 @@ export default class Visits extends React.Component {
return 'Current visitors'
}
+ if (this.showConversionRate()) {
+ return 'Conversions'
+ }
+
return 'Visitors'
}
diff --git a/assets/js/dashboard/stats/sources/referrer-list.js b/assets/js/dashboard/stats/sources/referrer-list.js
index 93b8d98c6..23e60014f 100644
--- a/assets/js/dashboard/stats/sources/referrer-list.js
+++ b/assets/js/dashboard/stats/sources/referrer-list.js
@@ -41,6 +41,10 @@ export default class Referrers extends React.Component {
return this.props.query.period === 'realtime'
}
+ showConversionRate() {
+ return !!this.props.query.filters.goal
+ }
+
fetchReferrers() {
if (this.props.query.filters.source) {
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/referrers/${encodeURIComponent(this.props.query.filters.source)}`, this.props.query, {show_noref: this.showNoRef()})
@@ -67,6 +71,7 @@ export default class Referrers extends React.Component {
}
renderReferrer(referrer) {
+ const maxWidthDeduction = this.showConversionRate() ? "10rem" : "5rem"
const query = new URLSearchParams(window.location.search)
query.set('referrer', referrer.name)
@@ -76,7 +81,7 @@ export default class Referrers extends React.Component {
count={referrer.count}
all={this.state.referrers}
bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15"
- maxWidthDeduction="4rem"
+ maxWidthDeduction={maxWidthDeduction}
>
@@ -95,12 +100,21 @@ export default class Referrers extends React.Component {
{numberFormatter(referrer.count)}
+ {this.showConversionRate() && {referrer.conversion_rate}%}
)
}
label() {
- return this.props.query.period === 'realtime' ? 'Current visitors' : 'Visitors'
+ if (this.props.query.period === 'realtime') {
+ return 'Current visitors'
+ }
+
+ if (this.showConversionRate()) {
+ return 'Conversions'
+ }
+
+ return 'Visitors'
}
renderList() {
@@ -108,10 +122,14 @@ export default class Referrers extends React.Component {
return (
Referrer
-
{ this.label() }
+
+
+ {this.label()}
+ {this.showConversionRate() && CR}
+
diff --git a/assets/js/dashboard/stats/sources/source-list.js b/assets/js/dashboard/stats/sources/source-list.js
index d75a88ec9..a8a02e827 100644
--- a/assets/js/dashboard/stats/sources/source-list.js
+++ b/assets/js/dashboard/stats/sources/source-list.js
@@ -77,7 +77,15 @@ class AllSources extends React.Component {
}
label() {
- return this.props.query.period === 'realtime' ? 'Current visitors' : 'Visitors'
+ if (this.props.query.period === 'realtime') {
+ return 'Current visitors'
+ }
+
+ if (this.showConversionRate()) {
+ return 'Conversions'
+ }
+
+ return 'Visitors'
}
renderList() {
@@ -198,7 +206,15 @@ class UTMSources extends React.Component {
}
label() {
- return this.props.query.period === 'realtime' ? 'Current visitors' : 'Visitors'
+ if (this.props.query.period === 'realtime') {
+ return 'Current visitors'
+ }
+
+ if (this.showConversionRate()) {
+ return 'Conversions'
+ }
+
+ return 'Visitors'
}
renderList() {
diff --git a/lib/plausible/stats/base.ex b/lib/plausible/stats/base.ex
index c4c7316e7..284b73f3d 100644
--- a/lib/plausible/stats/base.ex
+++ b/lib/plausible/stats/base.ex
@@ -311,6 +311,7 @@ defmodule Plausible.Stats.Base do
end
defp db_prop_val("referrer_source", @no_ref), do: ""
+ defp db_prop_val("referrer", @no_ref), do: ""
defp db_prop_val("utm_medium", @no_ref), do: ""
defp db_prop_val("utm_source", @no_ref), do: ""
defp db_prop_val("utm_campaign", @no_ref), do: ""
diff --git a/lib/plausible_web/controllers/api/stats_controller.ex b/lib/plausible_web/controllers/api/stats_controller.ex
index 3345f18d2..b058aa4ae 100644
--- a/lib/plausible_web/controllers/api/stats_controller.ex
+++ b/lib/plausible_web/controllers/api/stats_controller.ex
@@ -321,6 +321,7 @@ defmodule PlausibleWeb.Api.StatsController do
referrers =
Stats.breakdown(site, query, "visit:referrer", metrics, pagination)
+ |> maybe_add_cr(site, query, pagination, "referrer", "visit:referrer")
|> transform_keys(%{"referrer" => "name", "visitors" => "count"})
%{"visitors" => %{"value" => total_visitors}} = Stats.aggregate(site, query, ["visitors"])
@@ -608,6 +609,7 @@ defmodule PlausibleWeb.Api.StatsController do
without_goal = Enum.find(list_without_goals, fn s -> s[key_name] === item[key_name] end)
item
+ |> Map.put(:total_visitors, without_goal["visitors"])
|> Map.put(:conversion_rate, calculate_cr(without_goal["visitors"], item["visitors"]))
end)
end
diff --git a/test/plausible_web/controllers/api/stats_controller/browsers_test.exs b/test/plausible_web/controllers/api/stats_controller/browsers_test.exs
index 31d4b1569..1aca02344 100644
--- a/test/plausible_web/controllers/api/stats_controller/browsers_test.exs
+++ b/test/plausible_web/controllers/api/stats_controller/browsers_test.exs
@@ -32,7 +32,13 @@ defmodule PlausibleWeb.Api.StatsController.BrowsersTest do
conn = get(conn, "/api/stats/#{site.domain}/browsers?period=day&filters=#{filters}")
assert json_response(conn, 200) == [
- %{"name" => "Chrome", "count" => 1, "percentage" => 100, "conversion_rate" => 50.0}
+ %{
+ "name" => "Chrome",
+ "total_visitors" => 2,
+ "count" => 1,
+ "percentage" => 100,
+ "conversion_rate" => 50.0
+ }
]
end
end
diff --git a/test/plausible_web/controllers/api/stats_controller/countries_test.exs b/test/plausible_web/controllers/api/stats_controller/countries_test.exs
index 361621f06..be345032d 100644
--- a/test/plausible_web/controllers/api/stats_controller/countries_test.exs
+++ b/test/plausible_web/controllers/api/stats_controller/countries_test.exs
@@ -59,12 +59,14 @@ defmodule PlausibleWeb.Api.StatsController.CountriesTest do
assert json_response(conn, 200) == [
%{
"name" => "GBR",
+ "total_visitors" => 1,
"count" => 1,
"percentage" => 50,
"conversion_rate" => 100.0
},
%{
"name" => "EST",
+ "total_visitors" => 2,
"count" => 1,
"percentage" => 50,
"conversion_rate" => 50.0
diff --git a/test/plausible_web/controllers/api/stats_controller/operating_systems_test.exs b/test/plausible_web/controllers/api/stats_controller/operating_systems_test.exs
index 676c88020..8962b9e3e 100644
--- a/test/plausible_web/controllers/api/stats_controller/operating_systems_test.exs
+++ b/test/plausible_web/controllers/api/stats_controller/operating_systems_test.exs
@@ -33,7 +33,13 @@ defmodule PlausibleWeb.Api.StatsController.OperatingSystemsTest do
get(conn, "/api/stats/#{site.domain}/operating-systems?period=day&filters=#{filters}")
assert json_response(conn, 200) == [
- %{"name" => "Mac", "count" => 1, "percentage" => 100, "conversion_rate" => 50.0}
+ %{
+ "name" => "Mac",
+ "total_visitors" => 2,
+ "count" => 1,
+ "percentage" => 100,
+ "conversion_rate" => 50.0
+ }
]
end
end
diff --git a/test/plausible_web/controllers/api/stats_controller/pages_test.exs b/test/plausible_web/controllers/api/stats_controller/pages_test.exs
index e930827dc..468d1fbd1 100644
--- a/test/plausible_web/controllers/api/stats_controller/pages_test.exs
+++ b/test/plausible_web/controllers/api/stats_controller/pages_test.exs
@@ -95,7 +95,7 @@ defmodule PlausibleWeb.Api.StatsController.PagesTest do
conn = get(conn, "/api/stats/#{site.domain}/pages?period=day&filters=#{filters}")
assert json_response(conn, 200) == [
- %{"count" => 1, "name" => "/", "conversion_rate" => 33.3}
+ %{"total_visitors" => 3, "count" => 1, "name" => "/", "conversion_rate" => 33.3}
]
end
end
@@ -195,6 +195,7 @@ defmodule PlausibleWeb.Api.StatsController.PagesTest do
assert json_response(conn, 200) == [
%{
+ "total_visitors" => 1,
"count" => 1,
"entries" => 1,
"name" => "/page2",
@@ -202,6 +203,7 @@ defmodule PlausibleWeb.Api.StatsController.PagesTest do
"conversion_rate" => 100.0
},
%{
+ "total_visitors" => 2,
"count" => 1,
"entries" => 1,
"name" => "/page1",
@@ -289,6 +291,7 @@ defmodule PlausibleWeb.Api.StatsController.PagesTest do
%{
"name" => "/exit1",
"count" => 1,
+ "total_visitors" => 1,
"exits" => 1,
"exit_rate" => 50,
"conversion_rate" => 100.0
@@ -296,6 +299,7 @@ defmodule PlausibleWeb.Api.StatsController.PagesTest do
%{
"name" => "/exit2",
"count" => 1,
+ "total_visitors" => 1,
"exits" => 1,
"exit_rate" => 100,
"conversion_rate" => 100.0
diff --git a/test/plausible_web/controllers/api/stats_controller/screen_sizes_test.exs b/test/plausible_web/controllers/api/stats_controller/screen_sizes_test.exs
index f446595ce..22f15419e 100644
--- a/test/plausible_web/controllers/api/stats_controller/screen_sizes_test.exs
+++ b/test/plausible_web/controllers/api/stats_controller/screen_sizes_test.exs
@@ -34,6 +34,7 @@ defmodule PlausibleWeb.Api.StatsController.ScreenSizesTest do
assert json_response(conn, 200) == [
%{
"name" => "Desktop",
+ "total_visitors" => 2,
"count" => 1,
"percentage" => 100,
"conversion_rate" => 50.0
diff --git a/test/plausible_web/controllers/api/stats_controller/sources_test.exs b/test/plausible_web/controllers/api/stats_controller/sources_test.exs
index ab82603f9..6b8b947d3 100644
--- a/test/plausible_web/controllers/api/stats_controller/sources_test.exs
+++ b/test/plausible_web/controllers/api/stats_controller/sources_test.exs
@@ -310,7 +310,12 @@ defmodule PlausibleWeb.Api.StatsController.SourcesTest do
)
assert json_response(conn, 200) == [
- %{"name" => "Twitter", "count" => 1, "conversion_rate" => 50.0}
+ %{
+ "name" => "Twitter",
+ "total_visitors" => 2,
+ "count" => 1,
+ "conversion_rate" => 50.0
+ }
]
end
@@ -341,7 +346,12 @@ defmodule PlausibleWeb.Api.StatsController.SourcesTest do
)
assert json_response(conn, 200) == [
- %{"name" => "Twitter", "count" => 1, "conversion_rate" => 50.0}
+ %{
+ "name" => "Twitter",
+ "total_visitors" => 2,
+ "count" => 1,
+ "conversion_rate" => 50.0
+ }
]
end
end
@@ -487,7 +497,12 @@ defmodule PlausibleWeb.Api.StatsController.SourcesTest do
assert json_response(conn, 200) == %{
"total_visitors" => 1,
"referrers" => [
- %{"name" => "10words.com", "count" => 1}
+ %{
+ "name" => "10words.com",
+ "total_visitors" => 2,
+ "conversion_rate" => 50.0,
+ "count" => 1
+ }
]
}
end
@@ -523,7 +538,12 @@ defmodule PlausibleWeb.Api.StatsController.SourcesTest do
assert json_response(conn, 200) == %{
"total_visitors" => 1,
"referrers" => [
- %{"name" => "10words.com", "count" => 1}
+ %{
+ "name" => "10words.com",
+ "total_visitors" => 2,
+ "conversion_rate" => 50.0,
+ "count" => 1
+ }
]
}
end