analytics/assets/js/dashboard/query.js
Vignesh Joglekar 30ac901141
Adds manual, editable, auto-suggested filters, and negated&globbed path-based filters (#1121)
* Adds manual-filters + friends

commit 308192044d726e9a6f7406e333048ab3407aa260
Merge: 39287ab a299fab
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Fri Jun 11 03:40:55 2021 -0500

    Merge branch 'manual-filters' into manual-filters-2

commit a299fab1fb
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Fri Jun 11 02:20:26 2021 -0500

    Changes to split and pattern matched function for time_on_page

commit 10f10c933a
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Fri Jun 11 01:53:18 2021 -0500

    Fixes a couple of minor UX issues

commit f2e5ce8eb3
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Fri Jun 11 01:49:10 2021 -0500

    Fixes time on page for globbed and negated page paths

commit bb18af6526
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Thu Jun 10 05:24:05 2021 -0500

    Close to finalized version of updated version

    Just needs some additional testing + potentially code cleanup

commit d0b7bfe3da
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Fri May 28 04:21:21 2021 -0500

    Real Dialyzer Fix

commit 296a76af34
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Fri May 28 03:44:29 2021 -0500

    Dialyzer fix

commit 91f3b44017
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Thu May 27 17:11:24 2021 -0500

    Changelog

commit e041f75745
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Thu May 27 17:09:52 2021 -0500

    Formatting

commit f689642204
Merge: e00929b 4ff25f6
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Thu May 27 17:08:47 2021 -0500

    Merge branch 'master' into manual-filters

commit e00929b1c5
Merge: 83887c4 806975e
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Thu May 27 17:01:48 2021 -0500

    Merge branch 'manual-filters' of github.com:Vigasaurus/plausible-analytics into manual-filters

commit 83887c49cc
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Thu May 27 16:59:32 2021 -0500

    Adds tests for suggestions, formats goals suggestion query

commit 1cb7732a08
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Thu May 27 16:36:31 2021 -0500

    Adds goals as auto-complete capable filter

commit 4ca39cc406
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Thu May 27 16:08:22 2021 -0500

    95% completed auto-complete setup

    Still needs:
    - tests
    - goals added as filter

commit 22d38c4cd8
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Thu May 27 03:47:19 2021 -0500

    80% of auto-complete filters progress

    Still needs
    - countries and screen into new format
    - re-style dropdown and background
    - drop debounce time
    - tests

commit 806975ede9
Merge: 81c5e05 1a93542
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Tue May 25 15:28:21 2021 -0500

    Merge branch 'master' into manual-filters

commit 81c5e05760
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Tue May 25 15:21:03 2021 -0500

    Makes colorings on top bar elements consistent

commit fa7f6c2268
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Tue May 25 14:58:25 2021 -0500

    Makes requested changes, adds different version of filter button

commit 7dc65b9b7a
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Sat May 22 04:29:01 2021 -0500

    Changelog

commit c684f1c76a
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Sat May 22 04:26:14 2021 -0500

    Various UI Improvements

    - Makes edit buttons full-length & properly sized
    - Adds remove filter button in edit menu

commit a632e7a8ee
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Sat May 22 03:11:50 2021 -0500

    Adds tests for exclusions and wildcards

commit eb91a7942b
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Sat May 22 03:02:23 2021 -0500

    Fixes editing UX on list view

commit 6209d72aee
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Fri May 21 04:01:17 2021 -0500

    Bugfix in realtime view, formatting

commit 007d44df38
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Fri May 21 03:23:16 2021 -0500

    Second pass - mostly everything user-facing is done

    Still needs:
    - Tests
    - Potentially negating other filters
    - Potentially some code cleanup

commit cb7b5b9fbd
Author: Vignesh Joglekar <rexvigasaurus@gmail.com>
Date:   Fri May 21 01:49:52 2021 -0500

    First pass on manual filter & path regex/negated filters

    Still needs:
    - Form structure on filter modal
    - Edit filter button
    - Filter dropdown UI improvement
    - Filter modal mount data collection
    - Tests
    - Potentially negating other filters

* Fixes an issue between the updated React version and Flatpickr not showing up

Reverting to v16 was the simplest fix without diving into the issue upstream with Flatpickr

* Resolves comments on UI and code style

Co-authored-by: Uku Taht <Uku.taht@gmail.com>
2021-06-21 14:42:16 +03:00

181 lines
5.0 KiB
JavaScript

import React from 'react'
import { Link, withRouter } from 'react-router-dom'
import {formatDay, formatMonthYYYY, nowForSite, parseUTCDate} from './date'
import * as storage from './storage'
const PERIODS = ['realtime', 'day', 'month', '7d', '30d', '6mo', '12mo', 'custom']
export function parseQuery(querystring, site) {
const q = new URLSearchParams(querystring)
let period = q.get('period')
const periodKey = `period__${ site.domain}`
if (PERIODS.includes(period)) {
if (period !== 'custom' && period !== 'realtime') storage.setItem(periodKey, period)
} else if (storage.getItem(periodKey)) {
period = storage.getItem(periodKey)
} else {
period = '30d'
}
return {
period,
date: q.get('date') ? parseUTCDate(q.get('date')) : nowForSite(site),
from: q.get('from') ? parseUTCDate(q.get('from')) : undefined,
to: q.get('to') ? parseUTCDate(q.get('to')) : undefined,
filters: {
'goal': q.get('goal'),
'props': JSON.parse(q.get('props')),
'source': q.get('source'),
'utm_medium': q.get('utm_medium'),
'utm_source': q.get('utm_source'),
'utm_campaign': q.get('utm_campaign'),
'referrer': q.get('referrer'),
'screen': q.get('screen'),
'browser': q.get('browser'),
'browser_version': q.get('browser_version'),
'os': q.get('os'),
'os_version': q.get('os_version'),
'country': q.get('country'),
'page': q.get('page'),
'entry_page': q.get('entry_page'),
'exit_page': q.get('exit_page')
}
}
}
export function countFilters(query) {
let count = 0;
for (const filter of Object.values(query.filters)) {
if (filter) count++;
}
return count;
}
function generateQueryString(data) {
const query = new URLSearchParams(window.location.search)
Object.keys(data).forEach(key => {
if (!data[key]) {
query.delete(key)
return
}
query.set(key, data[key])
})
return query.toString()
}
export function navigateToQuery(history, queryFrom, newData) {
// if we update any data that we store in localstorage, make sure going back in history will
// revert them
if (newData.period && newData.period !== queryFrom.period) {
const replaceQuery = new URLSearchParams(window.location.search)
replaceQuery.set('period', queryFrom.period)
history.replace({ search: replaceQuery.toString() })
}
// then push the new query to the history
history.push({ search: generateQueryString(newData) })
}
class QueryLink extends React.Component {
constructor(props) {
super(props)
this.onClick = this.onClick.bind(this)
}
onClick(e) {
e.preventDefault()
navigateToQuery(this.props.history, this.props.query, this.props.to)
if (this.props.onClick) this.props.onClick(e)
}
render() {
const { history, query, to, ...props } = this.props
return (
<Link
{...props}
to={{ pathname: window.location.pathname, search: generateQueryString(to) }}
onClick={this.onClick}
/>
)
}
}
const QueryLinkWithRouter = withRouter(QueryLink)
export { QueryLinkWithRouter as QueryLink };
class QueryButton extends React.Component {
render() {
const { history, query, to, disabled, className, children } = this.props
return (
<button
className={className}
onClick={(event) => {
event.preventDefault()
navigateToQuery(history, query, to)
if (this.props.onClick) this.props.onClick(event)
history.push({ pathname: window.location.pathname, search: generateQueryString(to) })
}}
type="button"
disabled={disabled}
>
{children}
</button>
)
}
}
const QueryButtonWithRouter = withRouter(QueryButton)
export { QueryButtonWithRouter as QueryButton };
export function toHuman(query) {
if (query.period === 'day') {
return `on ${formatDay(query.date)}`
} if (query.period === 'month') {
return `in ${formatMonthYYYY(query.date)}`
} if (query.period === '7d') {
return 'in the last 7 days'
} if (query.period === '30d') {
return 'in the last 30 days'
} if (query.period === '6mo') {
return 'in the last 6 months'
} if (query.period === '12mo') {
return 'in the last 12 months'
}
}
export function removeQueryParam(search, parameter) {
const q = new URLSearchParams(search)
q.delete(parameter)
return q.toString()
}
export function eventName(query) {
if (query.filters.goal) {
if (query.filters.goal.startsWith('Visit ')) {
return 'pageviews'
}
return 'events'
}
return 'pageviews'
}
export const formattedFilters = {
'goal': 'Goal',
'props': 'Goal properties',
'source': 'Source',
'utm_medium': 'UTM Medium',
'utm_source': 'UTM Source',
'utm_campaign': 'UTM Campaign',
'referrer': 'Referrer URL',
'screen': 'Screen size',
'browser': 'Browser',
'browser_version': 'Browser Version',
'os': 'Operating System',
'os_version': 'Operating System Version',
'country': 'Country',
'page': 'Page',
'entry_page': 'Entry Page',
'exit_page': 'Exit Page'
}