Skip to content
Open
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
5 changes: 4 additions & 1 deletion assets/js/dashboard/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ export function dashboardStateToParams(
queryObj.to = formatISO(dashboardState.to)
}
if (dashboardState.filters) {
queryObj.filters = serializeApiFilters(dashboardState.filters)
queryObj.filters = serializeApiFilters(
dashboardState.filters,
dashboardState.engagedSessionsOnly
)
}
if (dashboardState.with_imported) {
queryObj.with_imported = String(dashboardState.with_imported)
Expand Down
17 changes: 14 additions & 3 deletions assets/js/dashboard/dashboard-state-context.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { createContext, useMemo, useContext, ReactNode } from 'react'
import React, {
createContext,
useMemo,
useContext,
ReactNode,
useState
} from 'react'
import { useLocation } from 'react-router'
import { useMountedEffect } from './custom-hooks'
import * as api from './api'
Expand All @@ -25,6 +31,7 @@ import { useSegmentsContext } from './filtering/segments-context'

const dashboardStateContextDefaultValue = {
dashboardState: dashboardStateDefaultValue,
setEngagedSessionsOnly: (_enabled: boolean) => {},
otherSearch: {} as Record<string, unknown>,
expandedSegment: null as (SavedSegment & { segment_data: SegmentData }) | null
}
Expand All @@ -49,6 +56,7 @@ export default function DashboardStateContextProvider({
SavedSegment & { segment_data: SegmentData }
>('expandedSegment')
const site = useSiteContext()
const [engagedSessionsOnly, setEngagedSessionsOnly] = useState(false)

const {
compare_from,
Expand Down Expand Up @@ -113,7 +121,8 @@ export default function DashboardStateContextProvider({
: defaultValues.with_imported,
filters,
resolvedFilters,
labels: (labels as FilterClauseLabels) || defaultValues.labels
labels: (labels as FilterClauseLabels) || defaultValues.labels,
engagedSessionsOnly
}
}, [
compare_from,
Expand All @@ -129,7 +138,8 @@ export default function DashboardStateContextProvider({
with_imported,
site,
expandedSegment,
segmentsContext.segments
segmentsContext.segments,
engagedSessionsOnly
])

useClearExpandedSegmentModeOnFilterClear({ expandedSegment, dashboardState })
Expand All @@ -148,6 +158,7 @@ export default function DashboardStateContextProvider({
<DashboardStateContext.Provider
value={{
dashboardState,
setEngagedSessionsOnly,
otherSearch,
expandedSegment
}}
Expand Down
4 changes: 3 additions & 1 deletion assets/js/dashboard/dashboard-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export type DashboardState = {
resolvedFilters: Filter[]
labels: FilterClauseLabels
with_imported: boolean
engagedSessionsOnly: boolean
}

export const dashboardStateDefaultValue: DashboardState = {
Expand All @@ -64,7 +65,8 @@ export const dashboardStateDefaultValue: DashboardState = {
filters: [],
resolvedFilters: [],
labels: {},
with_imported: true
with_imported: true,
engagedSessionsOnly: false
}

export type BreakdownResultMeta = {
Expand Down
23 changes: 23 additions & 0 deletions assets/js/dashboard/nav-menu/dashboard-options-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { isModifierPressed, isTyping, Keybind } from '../keybinding'
import { useMatch } from 'react-router-dom'
import { rootRoute } from '../router'
import { CsvExport, ExportStatus } from '../stats/csv-export/csv-export'
import { useSiteContext } from '../site-context'

function ImportedSwitchItem({ disabled }: { disabled: boolean }) {
const { dashboardState } = useDashboardStateContext()
Expand All @@ -41,8 +42,29 @@ function ImportedSwitchItem({ disabled }: { disabled: boolean }) {
)
}

function EngagedSessionsSwitchItem() {
const { dashboardState, setEngagedSessionsOnly } = useDashboardStateContext()

return (
<button
type="button"
onClick={() =>
setEngagedSessionsOnly(!dashboardState.engagedSessionsOnly)
}
className={classNames(
popover.items.classNames.navigationLink,
popover.items.classNames.hoverLink
)}
>
Only sessions with engagement
<Toggle on={dashboardState.engagedSessionsOnly} />
</button>
)
}

function DashboardOptionsMenuItems() {
const { dashboardState } = useDashboardStateContext()
const site = useSiteContext()
const { selectedInterval, onIntervalClick, availableIntervals } =
useGraphIntervalContext()
const imports = useImportsIncludedContext()
Expand Down Expand Up @@ -111,6 +133,7 @@ function DashboardOptionsMenuItems() {
exportStatus={exportStatus}
setExportStatus={setExportStatus}
/>
{site.engagedSessionsFilterAvailable && <EngagedSessionsSwitchItem />}
{imports.status === 'visible' && (
<>
<ImportedSwitchItem disabled={imports.disabled} />
Expand Down
2 changes: 2 additions & 0 deletions assets/js/dashboard/site-context.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('parseSiteFromDataset', () => {
data-legacy-time-on-page-cutoff="2022-01-01T00:00:00Z"
data-embedded=""
data-is-dbip="false"
data-engaged-sessions-filter-available="true"
data-current-user-role="owner"
data-current-user-id="1"
data-flags="{}"
Expand Down Expand Up @@ -60,6 +61,7 @@ describe('parseSiteFromDataset', () => {
embedded: false,
background: undefined,
isDbip: false,
engagedSessionsFilterAvailable: true,
flags: {},
shared: false,
isConsolidatedView: false,
Expand Down
3 changes: 3 additions & 0 deletions assets/js/dashboard/site-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export function parseSiteFromDataset(dataset: DOMStringMap): PlausibleSite {
embedded: dataset.embedded === 'true',
background: dataset.background,
isDbip: dataset.isDbip === 'true',
engagedSessionsFilterAvailable:
dataset.engagedSessionsFilterAvailable === 'true',
flags: JSON.parse(dataset.flags!),
shared: !!dataset.sharedLinkAuth,
isConsolidatedView: dataset.isConsolidatedView === 'true',
Expand Down Expand Up @@ -61,6 +63,7 @@ export const siteContextDefaultValue = {
embedded: false,
background: undefined as string | undefined,
isDbip: false,
engagedSessionsFilterAvailable: false,
flags: {} as FeatureFlags,
shared: false,
isConsolidatedView: false,
Expand Down
5 changes: 4 additions & 1 deletion assets/js/dashboard/stats-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ export function createStatsQuery(
dimensions: reportParams.dimensions || [],
metrics: reportParams.metrics,
filters: [
...remapToApiFilters(dashboardState.filters),
...remapToApiFilters(
dashboardState.filters,
dashboardState.engagedSessionsOnly
),
...(reportParams.alwaysOnFilters ?? [])
],
order_by: reportParams.order_by || null,
Expand Down
5 changes: 4 additions & 1 deletion assets/js/dashboard/stats/csv-export/csv-export-body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,10 @@ export function createCsvExportRequestBody(
return {
date_range: createDateRange(dashboardState),
relative_date: dashboardState.date ? formatISO(dashboardState.date) : null,
filters: remapToApiFilters(dashboardState.filters),
filters: remapToApiFilters(
dashboardState.filters,
dashboardState.engagedSessionsOnly
),
include: { imports: dashboardState.with_imported },
reports: reports
}
Expand Down
16 changes: 12 additions & 4 deletions assets/js/dashboard/util/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export const FILTER_OPERATIONS = {
has_not_done: 'has_not_done'
}

const ENGAGED_SESSIONS_API_FILTER = [
'has_done',
['is', 'event:name', ['engagement']]
]

export const FILTER_OPERATIONS_DISPLAY_NAMES = {
[FILTER_OPERATIONS.is]: 'is',
[FILTER_OPERATIONS.isNot]: 'is not',
Expand Down Expand Up @@ -240,8 +245,11 @@ function remapApiFilterKey(apiFilterKey) {
return apiFilterKey // maybe throw?
}

export function remapToApiFilters(filters) {
return filters.map(remapToApiFilter)
export function remapToApiFilters(filters, engagedSessionsOnly = false) {
const apiFilters = filters.map(remapToApiFilter)
return engagedSessionsOnly
? [...apiFilters, ENGAGED_SESSIONS_API_FILTER]
: apiFilters
}

export function remapFromApiFilters(apiFilters) {
Expand All @@ -260,8 +268,8 @@ export function remapFromApiFilters(apiFilters) {
})
}

export function serializeApiFilters(filters) {
return JSON.stringify(remapToApiFilters(filters))
export function serializeApiFilters(filters, engagedSessionsOnly = false) {
return JSON.stringify(remapToApiFilters(filters, engagedSessionsOnly))
}

function remapToApiFilter([operation, filterKey, clauses, ...modifiers]) {
Expand Down
6 changes: 6 additions & 0 deletions assets/js/dashboard/util/filters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,10 @@ describe(`${serializeApiFilters.name}`, () => {
JSON.stringify([['has_not_done', ['is', 'event:goal', ['Signup']]]])
)
})

it('adds the engaged sessions filter in API format', () => {
expect(serializeApiFilters([], true)).toEqual(
JSON.stringify([['has_done', ['is', 'event:name', ['engagement']]]])
)
})
})
1 change: 1 addition & 0 deletions assets/test-utils/app-context-providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const DEFAULT_SITE: PlausibleSite = {
embedded: false,
background: '',
isDbip: false,
engagedSessionsFilterAvailable: false,
flags: {},
shared: false,
isConsolidatedView: false,
Expand Down
3 changes: 3 additions & 0 deletions lib/plausible_web/templates/stats/stats.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
data-background={@conn.assigns[:background]}
data-is-dbip={to_string(@dbip?)}
data-current-user-role={@site_role}
data-engaged-sessions-filter-available={
to_string(Plausible.Auth.super_admin?(@conn.assigns[:current_user]))
}
data-current-user-id={
if user = @conn.assigns[:current_user], do: user.id, else: Jason.encode!(nil)
}
Expand Down
4 changes: 4 additions & 0 deletions test/plausible_web/controllers/stats_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,10 @@ defmodule PlausibleWeb.StatsControllerTest do
resp = html_response(conn, 200)

assert text_of_attr(resp, @react_container, "data-current-user-role") == "owner"

assert text_of_attr(resp, @react_container, "data-engaged-sessions-filter-available") ==
"true"

assert text_of_attr(resp, @react_container, "data-show-email-reports-cta") == "true"
end

Expand Down
Loading