analytics/lib/plausible_web/templates/stats/stats.html.heex
RobertJoonas e5b56dbe62
Refactor VisitorGraph (#3936)
* Give a more semantic name to a function

* Make the LineGraph component thinner

* Move LineGraph into a separate file

* Move interval logic into interval-picker.js

This commit also fixes a bug where the interval name displayed inside
the picker component flickers the default interval when the graph is
loading.

The problem was that we were counting on graphData for returning us the
current interval: `let currentInterval = graphData?.interval`

We should always know the default interval before making the main-graph
request. Sending graphData to IntervalPicker component does not make
sense anyway.

* extract data fetching functions out of VisitorGraph component

* Return graph_metric key from Top Stats API

This commit introduces no behavioral changes - only starts returning an
additional field, allowing us to avoid the following logic in React:

1. Finding the metric names, given a stat display name. E.g.
   `Unique visitors (last 30 min) -> visitors`

2. Checking if a metric is graphable or not

* Move metric state into localStorage

This commit gets rid of the internal `metric` state in the VisitorGraph
component and starts using localStorage for that instead.

This commit also chains the main-graph request into the top-stats request
callback - meaning that we'll always fetch new graph data after top stats
are updated. And we do it all in a single function.

Doing so simplifies the loading state significantly, and also helps to
make it clear, that at all times, existing top stats are required before
we can fetch the graph. That's because the metric is determined by which
Top stats are returned (for example, we can't be sure whether revenue
metrics will be returned or not).

* Make sure graph tooltip says "Converted Visitors"

* Extract a StatsExport function component

Again, instead of relying on `graphData?.interval` we can read it from
localStorage, or default to the largest interval available. The export
should not be dependant on the graph.

* Extract SamplingNotice function component

* Extract WithImportedSwitch function component

* Stop "lazy-loading" the graph and top stats

Since the container is always on top on the page, it will be visible on
the first render in any case - no matter the screen size.

* Turn VisitorGraph into a function component

* Display empty container until everything has loaded

* Do not display loading spinner on realtime ticks

* Turn Top Stats into a fn component

* fetch top stats and graph async

* Make sure revenue metrics can remain on the graph

* Add an extra check to canMetricBeGraphed

* fix typo

* remove redundant double negation
2024-04-04 13:39:55 +01:00

77 lines
3.4 KiB
Plaintext

<div class={stats_container_class(@conn)} data-site-domain={@site.domain}>
<PlausibleWeb.Components.FirstDashboardLaunchBanner.render site={@site} />
<%= if @site.locked do %>
<div
class="w-full px-4 py-4 text-sm font-bold text-center text-yellow-800 bg-yellow-100 rounded transition"
style="top: 91px"
role="alert"
>
<p>This dashboard is actually locked. You are viewing it with super-admin access</p>
</div>
<% end %>
<div class="pt-6"></div>
<div
id="stats-react-container"
style="overflow-anchor: none;"
data-domain={@site.domain}
data-offset={Plausible.Site.tz_offset(@site)}
data-has-goals={to_string(@has_goals)}
data-conversions-opted-out={to_string(Plausible.Billing.Feature.Goals.opted_out?(@site))}
data-funnels-opted-out={to_string(Plausible.Billing.Feature.Funnels.opted_out?(@site))}
data-props-opted-out={to_string(Plausible.Billing.Feature.Props.opted_out?(@site))}
data-funnels-available={
to_string(Plausible.Billing.Feature.Funnels.check_availability(@site.owner) == :ok)
}
data-props-available={
to_string(Plausible.Billing.Feature.Props.check_availability(@site.owner) == :ok)
}
data-revenue-goals={Jason.encode!(@revenue_goals)}
data-funnels={Jason.encode!(@funnels)}
data-has-props={to_string(@has_props)}
data-logged-in={to_string(!!@conn.assigns[:current_user])}
data-stats-begin={@stats_start_date}
data-native-stats-begin={@native_stats_start_date}
data-shared-link-auth={assigns[:shared_link_auth]}
data-embedded={to_string(@conn.assigns[:embedded])}
data-background={@conn.assigns[:background]}
data-is-dbip={to_string(@is_dbip)}
data-current-user-role={@conn.assigns[:current_user_role]}
data-flags={Jason.encode!(@flags)}
data-valid-intervals-by-period={
Plausible.Stats.Interval.valid_by_period(site: @site) |> Jason.encode!()
}
>
</div>
<div id="modal_root"></div>
<%= if !@conn.assigns[:current_user] && @conn.assigns[:demo] do %>
<div class="bg-gray-50 dark:bg-gray-850">
<div class="py-12 lg:py-16 lg:flex lg:items-center lg:justify-between">
<h2 class="text-3xl font-extrabold tracking-tight text-gray-900 leading-9 sm:text-4xl sm:leading-10 dark:text-gray-100">
Want these stats for your website? <br />
<span class="text-indigo-600">Start your free trial today.</span>
</h2>
<div class="flex mt-8 lg:flex-shrink-0 lg:mt-0">
<div class="inline-flex shadow rounded-md">
<a
href="/register"
class="inline-flex items-center justify-center px-5 py-3 text-base font-medium text-white bg-indigo-600 border border-transparent leading-6 rounded-md hover:bg-indigo-500 focus:outline-none focus:ring transition duration-150 ease-in-out"
>
Get started
</a>
</div>
<div class="inline-flex ml-3 shadow rounded-md">
<a
href="/"
class="inline-flex items-center justify-center px-5 py-3 text-base font-medium text-indigo-600 bg-white border border-transparent leading-6 rounded-md dark:text-gray-100 dark:bg-gray-800 hover:text-indigo-500 dark:hover:text-indigo-500 focus:outline-none focus:ring transition duration-150 ease-in-out"
>
Learn more
</a>
</div>
</div>
</div>
</div>
<% end %>
</div>