Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 1 addition & 20 deletions assets/js/dashboard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useMemo, useState, useEffect, useCallback } from 'react'
import { LiveViewPortal } from './components/liveview-portal'

Check failure on line 2 in assets/js/dashboard/index.tsx

View workflow job for this annotation

GitHub Actions / Build and test

'LiveViewPortal' is defined but never used. Allowed unused vars must match /^_/u
import VisitorGraph from './stats/graph/visitor-graph'
import Sources from './stats/sources'
import Pages from './stats/pages'
Expand All @@ -9,10 +9,10 @@
import Behaviours from './stats/behaviours'
import { useQueryContext } from './query-context'
import { useSiteContext } from './site-context'
import { hasConversionGoalFilter, isRealTimeDashboard } from './util/filters'

Check failure on line 12 in assets/js/dashboard/index.tsx

View workflow job for this annotation

GitHub Actions / Build and test

'hasConversionGoalFilter' is defined but never used. Allowed unused vars must match /^_/u
import { useAppNavigate } from './navigation/use-app-navigate'
import { parseSearch } from './util/url-search-params'
import { getDomainScopedStorageKey } from './util/storage'

Check failure on line 15 in assets/js/dashboard/index.tsx

View workflow job for this annotation

GitHub Actions / Build and test

'getDomainScopedStorageKey' is defined but never used. Allowed unused vars must match /^_/u

function DashboardStats({
importedDataInView,
Expand All @@ -22,8 +22,8 @@
updateImportedDataInView?: (v: boolean) => void
}) {
const navigate = useAppNavigate()
const site = useSiteContext()

Check failure on line 25 in assets/js/dashboard/index.tsx

View workflow job for this annotation

GitHub Actions / Build and test

'site' is assigned a value but never used. Allowed unused vars must match /^_/u
const { query } = useQueryContext()

Check failure on line 26 in assets/js/dashboard/index.tsx

View workflow job for this annotation

GitHub Actions / Build and test

'query' is assigned a value but never used. Allowed unused vars must match /^_/u

// Handler for navigation events delegated from LiveView dashboard.
// Necessary to emulate navigation events in LiveView with pushState
Expand Down Expand Up @@ -56,26 +56,7 @@
<>
<VisitorGraph updateImportedDataInView={updateImportedDataInView} />
<Sources />
{site.flags.live_dashboard ? (
<LiveViewPortal
id="pages-breakdown-live"
tabs={[
{
label: hasConversionGoalFilter(query)
? 'Conversion pages'
: 'Top pages',
value: 'pages'
},
{ label: 'Entry pages', value: 'entry-pages' },
{ label: 'Exit pages', value: 'exit-pages' }
]}
storageKey={getDomainScopedStorageKey('pageTab', site.domain)}
className="w-full h-full border-0 overflow-hidden"
/>
) : (
<Pages />
)}

<Pages />
<Locations />
<Devices />
<Behaviours importedDataInView={importedDataInView} />
Expand Down
80 changes: 80 additions & 0 deletions assets/js/liveview/datepicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Hook widget for optimistic updates to the datepicker
* label when relative date is changed (prev/next period
* arrow keys).
*/

import { buildHook } from './hook_builder'

function prevPeriod() {
if (this.currentIndex === 0) {
return false
} else {
this.currentIndex--
return true
}
}

function nextPeriod() {
if (this.currentIndex === this.dates.length - 1) {
return false
} else {
this.currentIndex++
return true
}
}

function debounce(fn, delay) {
let timer

return function (...args) {
clearTimeout(timer)

timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}

export default buildHook({
initialize() {
this.currentIndex = parseInt(this.el.dataset.currentIndex)
this.dates = JSON.parse(this.el.dataset.dates)
this.labels = JSON.parse(this.el.dataset.labels)

this.prevPeriodButton = this.el.querySelector('button#prev-period')
this.nextPeriodButton = this.el.querySelector('button#next-period')
this.periodLabel = this.el.querySelector('#period-label')

this.debouncedPushEvent = debounce(() => {
this.pushEventTo(this.el.dataset.target, 'set-relative-date', {
date: this.dates[this.currentIndex]
})
}, 500)

this.addListener('click', this.el, (e) => {
if (this.dates.length) {
const button = e.target.closest('button')

let updated = false

if (button === this.prevPeriodButton) {
updated = prevPeriod.bind(this)()
}

if (button === this.nextPeriodButton) {
nextPeriod.bind(this)()
updated = nextPeriod.bind(this)()
}

if (updated) {
this.debouncedPushEvent()
}

this.periodLabel.innerText = this.labels[this.currentIndex]
this.prevPeriodButton.dataset.disabled = `${this.currentIndex == 0}`
this.nextPeriodButton.dataset.disabled = `${this.currentIndex == this.dates.length - 1}`
}
})
}
})
4 changes: 3 additions & 1 deletion assets/js/liveview/live_socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { LiveSocket } from 'phoenix_live_view'
import { Modal, Dropdown } from 'prima'
import DashboardRoot from './dashboard_root'
import DashboardTabs from './dashboard_tabs.js'
import DatePicker from './datepicker'
import topbar from 'topbar'
/* eslint-enable import/no-unresolved */

Expand All @@ -22,7 +23,7 @@ let disablePushStateFlag = document.querySelector(
)
let domain = document.querySelector("meta[name='dashboard-domain']")
if (csrfToken && websocketUrl) {
let Hooks = { Modal, Dropdown, DashboardRoot, DashboardTabs }
let Hooks = { Modal, Dropdown, DashboardRoot, DashboardTabs, DatePicker }
Hooks.Metrics = {
mounted() {
this.handleEvent('send-metrics', ({ event_name }) => {
Expand Down Expand Up @@ -80,6 +81,7 @@ if (csrfToken && websocketUrl) {
// user preferences across the reloads.
user_prefs: {
pages_tab: localStorage.getItem(`pageTab__${domainName}`),
sources_tab: localStorage.getItem(`sourceTab__${domainName}`),
period: localStorage.getItem(`period__${domainName}`),
comparison: localStorage.getItem(`comparison_mode__${domainName}`),
match_day_of_week: localStorage.getItem(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ defmodule PlausibleWeb.CustomerSupport.Site.Components.Overview do

<.styled_link
new_tab={true}
href={Routes.stats_path(PlausibleWeb.Endpoint, :stats, @site.domain, [])}
href={Routes.stats_path(PlausibleWeb.Endpoint, :dashboard, @site.domain, [])}
>
Dashboard
</.styled_link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ defmodule PlausibleWeb.CustomerSupport.Team.Components.ConsolidatedViews do
<.td>
<.styled_link
new_tab={true}
href={Routes.stats_path(PlausibleWeb.Endpoint, :stats, consolidated_view.domain, [])}
href={
Routes.stats_path(PlausibleWeb.Endpoint, :dashboard, consolidated_view.domain, [])
}
>
Dashboard
</.styled_link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ defmodule PlausibleWeb.CustomerSupport.Team.Components.Sites do
<.td>
<.styled_link
new_tab={true}
href={Routes.stats_path(PlausibleWeb.Endpoint, :stats, site.domain, [])}
href={Routes.stats_path(PlausibleWeb.Endpoint, :dashboard, site.domain, [])}
>
Dashboard
</.styled_link>
Expand Down
2 changes: 1 addition & 1 deletion extra/lib/plausible_web/live/verification.ex
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ defmodule PlausibleWeb.Live.Verification do
end

defp redirect_to_stats(socket) do
stats_url = Routes.stats_path(PlausibleWeb.Endpoint, :stats, socket.assigns.domain, [])
stats_url = Routes.stats_path(PlausibleWeb.Endpoint, :dashboard, socket.assigns.domain, [])
redirect(socket, to: stats_url)
end

Expand Down
53 changes: 53 additions & 0 deletions lib/plausible/stats/dashboard/periods.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
defmodule Plausible.Stats.Dashboard.Periods do
@moduledoc false

@all [
{"realtime", :realtime},
{"day", :day},
{"month", :month},
{"year", :year},
{"all", :all},
{"7d", {:last_n_days, 7}},
{"28d", {:last_n_days, 28}},
{"30d", {:last_n_days, 30}},
{"91d", {:last_n_days, 91}},
{"6mo", {:last_n_months, 6}},
{"12mo", {:last_n_months, 12}}
]

def all(), do: @all

@shorthands Enum.map(@all, &elem(&1, 0))

def shorthands(), do: @shorthands

@input_date_ranges Map.new(@all)

def input_date_ranges(), do: @input_date_ranges

def input_date_range_for(period), do: @input_date_ranges[period]

def label_for(:day, %Date{} = date) do
Calendar.strftime(date, "%a, %-d %b")
end

def label_for(:month, %Date{} = date) do
Calendar.strftime(date, "%B %Y")
end

def label_for(:year, %Date{} = date) do
Calendar.strftime(date, "Year of %Y")
end

def label_for(:realtime, _date), do: "Realtime"
def label_for(:day, _date), do: "Today"
def label_for(:month, _date), do: "Month to date"
def label_for(:year, _date), do: "Year to date"
def label_for(:all, _date), do: "All"
def label_for({:last_n_days, 7}, _date), do: "Last 7 days"
def label_for({:last_n_days, 28}, _date), do: "Last 28 days"
def label_for({:last_n_days, 30}, _date), do: "Last 30 days"
def label_for({:last_n_days, 91}, _date), do: "Last 91 days"
def label_for({:last_n_months, 6}, _date), do: "Last 6 months"
def label_for({:last_n_months, 12}, _date), do: "Last 12 months"
end
25 changes: 6 additions & 19 deletions lib/plausible/stats/dashboard/query_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Plausible.Stats.Dashboard.QueryParser do
to be filled in by each specific report.
"""

alias Plausible.Stats.Dashboard
alias Plausible.Stats.{ParsedQueryParams, QueryInclude}

@default_include %QueryInclude{
Expand All @@ -29,21 +30,7 @@ defmodule Plausible.Stats.Dashboard.QueryParser do

def default_pagination(), do: @default_pagination

@valid_period_shorthands %{
"realtime" => :realtime,
"day" => :day,
"month" => :month,
"year" => :year,
"all" => :all,
"7d" => {:last_n_days, 7},
"28d" => {:last_n_days, 28},
"30d" => {:last_n_days, 30},
"91d" => {:last_n_days, 91},
"6mo" => {:last_n_months, 6},
"12mo" => {:last_n_months, 12}
}

@valid_period_shorthand_keys Map.keys(@valid_period_shorthands)
@period_shorthands Dashboard.Periods.shorthands()

@valid_comparison_shorthands %{
"previous_period" => :previous_period,
Expand Down Expand Up @@ -78,8 +65,8 @@ defmodule Plausible.Stats.Dashboard.QueryParser do
end

defp parse_input_date_range(%{"period" => period}, _site, _user_prefs)
when period in @valid_period_shorthand_keys do
@valid_period_shorthands[period]
when period in @period_shorthands do
Dashboard.Periods.input_date_range_for(period)
end

defp parse_input_date_range(
Expand All @@ -93,8 +80,8 @@ defmodule Plausible.Stats.Dashboard.QueryParser do
end

defp parse_input_date_range(_params, _site, %{"period" => period})
when period in @valid_period_shorthand_keys do
@valid_period_shorthands[period]
when period in @period_shorthands do
Dashboard.Periods.input_date_range_for(period)
end

defp parse_input_date_range(_params, site, _user_prefs) do
Expand Down
6 changes: 6 additions & 0 deletions lib/plausible/stats/dashboard/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ defmodule Plausible.Stats.Dashboard.Utils do
filter -> ParsedQueryParams.add_or_replace_filter(params, filter)
end

params =
case Keyword.get(opts, :update_params) do
nil -> params
update_params -> ParsedQueryParams.set(params, update_params)
end

query_string =
case Dashboard.QuerySerializer.serialize(params) do
"" -> ""
Expand Down
6 changes: 5 additions & 1 deletion lib/plausible_web/components/prima_dropdown.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ defmodule PlausibleWeb.Components.PrimaDropdown do
use Phoenix.Component

@dropdown_item_icon_base_class "text-gray-600 dark:text-gray-400 group-hover/item:text-gray-900 group-data-focus/item:text-gray-900 dark:group-hover/item:text-gray-100 dark:group-data-focus/item:text-gray-100"
@dropdown_menu_default_class "bg-white rounded-md shadow-lg ring-1 ring-black/5 focus:outline-none p-1.5 dark:bg-gray-800"

defdelegate dropdown(assigns), to: Prima.Dropdown
defdelegate dropdown_trigger(assigns), to: Prima.Dropdown

attr(:class, :string, default: @dropdown_menu_default_class)
slot(:inner_block, required: true)

# placement: bottom-end should probably be default in prima. Feels more natural
Expand All @@ -16,7 +18,7 @@ defmodule PlausibleWeb.Components.PrimaDropdown do
~H"""
<Dropdown.dropdown_menu
placement="bottom-end"
class="bg-white rounded-md shadow-lg ring-1 ring-black/5 focus:outline-none p-1.5 dark:bg-gray-800"
class={@class}
>
{render_slot(@inner_block)}
</Dropdown.dropdown_menu>
Expand All @@ -25,6 +27,7 @@ defmodule PlausibleWeb.Components.PrimaDropdown do

attr(:as, :any, default: nil)
attr(:disabled, :boolean, default: false)
attr(:args, :map, default: %{})
attr(:rest, :global, include: ~w(navigate patch href))
slot(:inner_block, required: true)

Expand All @@ -34,6 +37,7 @@ defmodule PlausibleWeb.Components.PrimaDropdown do
as={@as}
disabled={@disabled}
class="group/item z-50 flex items-center gap-x-2 min-w-max w-full rounded-md pl-3 pr-5 py-2 text-gray-700 text-sm dark:text-gray-300 data-focus:bg-gray-100 dark:data-focus:bg-gray-700 data-focus:text-gray-900 dark:data-focus:text-gray-100"
args={@args}
{@rest}
>
{render_slot(@inner_block)}
Expand Down
2 changes: 1 addition & 1 deletion lib/plausible_web/controllers/invitation_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ defmodule PlausibleWeb.InvitationController do
if site do
conn
|> put_flash(:success, "You now have access to #{site.domain}")
|> redirect(to: Routes.stats_path(conn, :stats, site.domain, []))
|> redirect(to: Routes.stats_path(conn, :dashboard, site.domain, []))
else
conn
|> put_flash(:success, "You now have access to \"#{team.name}\" team")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ defmodule PlausibleWeb.Site.MembershipController do
redirect_target =
if guest_membership.team_membership.user_id == current_user.id and
guest_membership.role == :viewer do
Routes.stats_path(conn, :stats, site.domain, [])
Routes.stats_path(conn, :dashboard, site.domain, [])
else
Routes.site_path(conn, :settings_people, site.domain)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/plausible_web/controllers/stats_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ defmodule PlausibleWeb.StatsController do
)

if shared_link do
new_link_format = Routes.stats_path(conn, :shared_link, shared_link.site.domain, auth: slug)
new_link_format = Routes.stats_path(conn, :dashboard, shared_link.site.domain, auth: slug)
redirect(conn, to: new_link_format)
else
render_error(conn, 404)
Expand Down
2 changes: 1 addition & 1 deletion lib/plausible_web/live/awaiting_pageviews.ex
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ defmodule PlausibleWeb.Live.AwaitingPageviews do
end

defp redirect_to_stats(socket) do
stats_url = Routes.stats_path(PlausibleWeb.Endpoint, :stats, socket.assigns.domain, [])
stats_url = Routes.stats_path(PlausibleWeb.Endpoint, :dashboard, socket.assigns.domain, [])
redirect(socket, to: stats_url)
end

Expand Down
Loading
Loading