Skip to content

Commit

Permalink
Improve dashboard main graph
Browse files Browse the repository at this point in the history
- Graph tooltip for revenue metrics now shows revenue with the relevant
  monetary unit.
- Graph tooltips for scroll depth, visit duration and so on now shows `-`
  instead of 0 on dates where no relevant visits occurred.
  • Loading branch information
macobo committed Mar 6, 2025
1 parent 25a0456 commit 13e5474
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 103 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ All notable changes to this project will be documented in this file.
- Improved report performance in cases where site has a lot of unique pathnames
- Plausible script now uses `fetch` with keepalive flag as default over `XMLHttpRequest`. This will ensure more reliable tracking. Reminder to use `compat` script variant if tracking Internet Explorer is required.
- The old `/api/health` healtcheck is soft-deprecated in favour of separate `/api/system/health/live` and `/api/system/health/ready` checks
- Main graph now shows revenue with relevant currency symbol when hovering a data point
- Main graph now shows `-` instead of `0` for visit duration, scroll depth when hovering a data point with no visit data

### Fixed

Expand Down
13 changes: 7 additions & 6 deletions assets/js/dashboard/stats/graph/graph-tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@ const buildTooltipData = function(query, graphData, metric, tooltipModel) {
const label = data && renderBucketLabel(query, graphData, graphData.labels[data.dataIndex])
const comparisonLabel = comparisonData && renderBucketLabel(query, graphData, graphData.comparison_labels[comparisonData.dataIndex], true)

const value = data?.raw || 0
const comparisonValue = comparisonData?.raw || 0
const comparisonDifference = label && comparisonLabel && calculatePercentageDifference(comparisonValue, value)
const value = graphData.plot[data.dataIndex]

const metricFormatter = MetricFormatterShort[metric]
const formattedValue = metricFormatter(value)
const formattedComparisonValue = comparisonData && metricFormatter(comparisonValue)
const formatter = MetricFormatterShort[metric]
const comparisonValue = graphData.comparison_plot?.[comparisonData.dataIndex]
const comparisonDifference = label && comparisonData && calculatePercentageDifference(comparisonValue, value)

const formattedValue = formatter(value)
const formattedComparisonValue = comparisonData && formatter(comparisonValue)

return { label, formattedValue, comparisonLabel, formattedComparisonValue, comparisonDifference }
}
Expand Down
18 changes: 15 additions & 3 deletions assets/js/dashboard/stats/graph/graph-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,25 @@ export const METRIC_LABELS = {
'conversion_rate': 'Conversion Rate',
'average_revenue': 'Average Revenue',
'total_revenue': 'Total Revenue',
'scroll_depth': 'Scroll Depth',
}

function plottable(dataArray) {
return dataArray?.map((value) => {
if (typeof value === 'object' && value !== null) {
// Revenue metrics are returned as objects with a `value` property
return value.value
}

return value || 0
})
}

const buildComparisonDataset = function(comparisonPlot) {
if (!comparisonPlot) return []

return [{
data: comparisonPlot,
data: plottable(comparisonPlot),
borderColor: 'rgba(60,70,110,0.2)',
pointBackgroundColor: 'rgba(60,70,110,0.2)',
pointHoverBackgroundColor: 'rgba(60, 70, 110)',
Expand All @@ -55,7 +67,7 @@ const buildDashedDataset = function(plot, presentIndex) {
const dashedPlot = (new Array(presentIndex - 1)).concat(dashedPart)

return [{
data: dashedPlot,
data: plottable(dashedPlot),
borderDash: [3, 3],
borderColor: 'rgba(101,116,205)',
pointHoverBackgroundColor: 'rgba(71, 87, 193)',
Expand All @@ -67,7 +79,7 @@ const buildMainPlotDataset = function(plot, presentIndex) {
const data = presentIndex ? plot.slice(0, presentIndex) : plot

return [{
data: data,
data: plottable(data),
borderColor: 'rgba(101,116,205)',
pointBackgroundColor: 'rgba(101,116,205)',
pointHoverBackgroundColor: 'rgba(71, 87, 193)',
Expand Down
8 changes: 4 additions & 4 deletions assets/js/dashboard/stats/reports/metric-formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ export const MetricFormatterShort: Record<

conversions: numberShortFormatter,

time_on_page: durationFormatter,
visit_duration: durationFormatter,
time_on_page: nullable(durationFormatter),
visit_duration: nullable(durationFormatter),

bounce_rate: percentageFormatter,
conversion_rate: percentageFormatter,
Expand All @@ -62,8 +62,8 @@ export const MetricFormatterLong: Record<

conversions: numberLongFormatter,

time_on_page: durationFormatter,
visit_duration: durationFormatter,
time_on_page: nullable(durationFormatter),
visit_duration: nullable(durationFormatter),

bounce_rate: percentageFormatter,
conversion_rate: percentageFormatter,
Expand Down
53 changes: 39 additions & 14 deletions lib/plausible/stats/timeseries.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Plausible.Stats.Timeseries do
Avoid adding new logic here - update QueryBuilder etc instead.
"""

use Plausible
use Plausible.ClickhouseRepo
alias Plausible.Stats.{Comparisons, Query, QueryRunner, QueryOptimizer, Time}

Expand Down Expand Up @@ -70,28 +71,52 @@ defmodule Plausible.Stats.Timeseries do
Map.get(
results_map,
key,
empty_row(key, query.metrics)
empty_row(key, query.metrics, query)
)
end)
|> transform_realtime_labels(query)
|> transform_keys(%{group_conversion_rate: :conversion_rate})
end

defp empty_row(date, metrics) do
defp empty_row(date, metrics, query) do
Enum.reduce(metrics, %{date: date}, fn metric, row ->
case metric do
:pageviews -> Map.merge(row, %{pageviews: 0})
:events -> Map.merge(row, %{events: 0})
:visitors -> Map.merge(row, %{visitors: 0})
:visits -> Map.merge(row, %{visits: 0})
:views_per_visit -> Map.merge(row, %{views_per_visit: 0.0})
:conversion_rate -> Map.merge(row, %{conversion_rate: 0.0})
:group_conversion_rate -> Map.merge(row, %{group_conversion_rate: 0.0})
:scroll_depth -> Map.merge(row, %{scroll_depth: nil})
:bounce_rate -> Map.merge(row, %{bounce_rate: 0.0})
:visit_duration -> Map.merge(row, %{visit_duration: nil})
:average_revenue -> Map.merge(row, %{average_revenue: 0.0})
:total_revenue -> Map.merge(row, %{total_revenue: 0.0})
:pageviews ->
Map.merge(row, %{pageviews: 0})

:events ->
Map.merge(row, %{events: 0})

:visitors ->
Map.merge(row, %{visitors: 0})

:visits ->
Map.merge(row, %{visits: 0})

:views_per_visit ->
Map.merge(row, %{views_per_visit: 0.0})

:conversion_rate ->
Map.merge(row, %{conversion_rate: 0.0})

:group_conversion_rate ->
Map.merge(row, %{group_conversion_rate: 0.0})

:scroll_depth ->
Map.merge(row, %{scroll_depth: nil})

:bounce_rate ->
Map.merge(row, %{bounce_rate: 0.0})

:visit_duration ->
Map.merge(row, %{visit_duration: nil})

metric when metric in [:average_revenue, :total_revenue] ->
on_ee do
value = Plausible.Stats.Goal.Revenue.format_revenue_metric(nil, query, [date])

Map.merge(row, %{metric => value})
end
end
end)
end
Expand Down
8 changes: 1 addition & 7 deletions lib/plausible_web/controllers/api/stats_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,7 @@ defmodule PlausibleWeb.Api.StatsController do
end

defp plot_timeseries(timeseries, metric) do
Enum.map(timeseries, fn row ->
case row[metric] do
nil -> 0
%{value: value} -> value
value -> value
end
end)
Enum.map(timeseries, & &1[metric])
end

defp label_timeseries(main_result, nil) do
Expand Down
Loading

0 comments on commit 13e5474

Please sign in to comment.