analytics/assets/js/dashboard/stats/behaviours/prop-breakdown.js

213 lines
6.6 KiB
JavaScript
Raw Normal View History

import React from 'react';
import { Link } from 'react-router-dom'
import * as storage from '../../util/storage'
import Bar from '../bar'
import numberFormatter from '../../util/number-formatter'
import * as api from '../../api'
const MOBILE_UPPER_WIDTH = 767
const DEFAULT_WIDTH = 1080
Realtime dashboard improvements (#2445) * add a new realtime-update-timer module * hook to the new 'tick' event in ListReport for auto-updates This commit fixes the bug where all reports using the `ListReport` component did not auto-update in realtime mode. Those reports are: - Pages (Top / Entry / Exit) - Locations (Countries / Regions / Cities) - Devices (Screen Sizes / Browsers + versions / OS-s + versions) * fetch data for ListReports only when scrolled into view * refactor fetching data in ListReport * refer to one source of truth for utm tags * make the 'All' tab in Sources auto-update * make all UTM tabs in Sources auto-update * fetch UTM data only when scrolled into view * auto-update Referrers with the new timer * auto-update google search terms * auto-update Conversions * make countries map auto-update * auto-update visitor-graph and top stats with new timer * use new tick event for current visitors (in Historical) * remove the old timer class * update changelog * Visual improvements to automatic realtime updates (#2532) * minor consistency fix for text color in dark mode * use FlipMove in goal conversions report * use FlipMove in ListReports * set main graph and top stats loading state correctly * refactor isIntervalValid function * enforce intervals are valid when set and stored * remove duplicate data fetching on interval change Fetching new data is handled by the `fetchGraphData` callback in `updateInterval` * refactor updateMetric function * make it clearer why 'metric' can be a faulty value * extract 'query' and 'site' variables from 'this.props' * reset interval state only when period is changed The 'maybeRollbackInterval' function was also used to fetch data. This commit replaces all those function calls with 'fetchGraphData' which better describes the actual behavior. We should only worry about rolling back the interval if 'query.period' has changed. This commit also stops the graph from flickering when it is updated in realtime. * update names of two variables * remove unnecessary negation * make collapsed graph state more explicit * consider stored invalid intervals when graph mounts * fix not showing loading spinner regression * remove interval state from VisitorGraph (#2540) * Realtime prop breakdown (#2535) * disable load more in realtime mode * extract doFetch function * separate fetchPropBreakdown and fetchNextPage functions * subscribe for auto-updates in realtime * improve readability with function name changes
2023-01-02 18:42:57 +03:00
const BREAKDOWN_LIMIT = 100
2021-08-24 15:23:01 +03:00
// https://stackoverflow.com/a/43467144
function isValidHttpUrl(string) {
let url;
try {
url = new URL(string);
} catch (_) {
return false;
}
return url.protocol === "http:" || url.protocol === "https:";
}
2020-10-30 11:49:41 +03:00
export default class PropertyBreakdown extends React.Component {
constructor(props) {
super(props)
2020-10-30 11:49:41 +03:00
let propKey = props.goal.prop_names[0]
this.storageKey = 'goalPropTab__' + props.site.domain + props.goal.name
const storedKey = storage.getItem(this.storageKey)
2020-10-30 11:49:41 +03:00
if (props.goal.prop_names.includes(storedKey)) {
propKey = storedKey
}
2020-10-30 11:49:41 +03:00
if (props.query.filters['props']) {
propKey = Object.keys(props.query.filters['props'])[0]
}
this.state = {
loading: true,
propKey: propKey,
viewport: DEFAULT_WIDTH,
breakdown: [],
page: 1,
moreResultsAvailable: false
}
this.handleResize = this.handleResize.bind(this);
Realtime dashboard improvements (#2445) * add a new realtime-update-timer module * hook to the new 'tick' event in ListReport for auto-updates This commit fixes the bug where all reports using the `ListReport` component did not auto-update in realtime mode. Those reports are: - Pages (Top / Entry / Exit) - Locations (Countries / Regions / Cities) - Devices (Screen Sizes / Browsers + versions / OS-s + versions) * fetch data for ListReports only when scrolled into view * refactor fetching data in ListReport * refer to one source of truth for utm tags * make the 'All' tab in Sources auto-update * make all UTM tabs in Sources auto-update * fetch UTM data only when scrolled into view * auto-update Referrers with the new timer * auto-update google search terms * auto-update Conversions * make countries map auto-update * auto-update visitor-graph and top stats with new timer * use new tick event for current visitors (in Historical) * remove the old timer class * update changelog * Visual improvements to automatic realtime updates (#2532) * minor consistency fix for text color in dark mode * use FlipMove in goal conversions report * use FlipMove in ListReports * set main graph and top stats loading state correctly * refactor isIntervalValid function * enforce intervals are valid when set and stored * remove duplicate data fetching on interval change Fetching new data is handled by the `fetchGraphData` callback in `updateInterval` * refactor updateMetric function * make it clearer why 'metric' can be a faulty value * extract 'query' and 'site' variables from 'this.props' * reset interval state only when period is changed The 'maybeRollbackInterval' function was also used to fetch data. This commit replaces all those function calls with 'fetchGraphData' which better describes the actual behavior. We should only worry about rolling back the interval if 'query.period' has changed. This commit also stops the graph from flickering when it is updated in realtime. * update names of two variables * remove unnecessary negation * make collapsed graph state more explicit * consider stored invalid intervals when graph mounts * fix not showing loading spinner regression * remove interval state from VisitorGraph (#2540) * Realtime prop breakdown (#2535) * disable load more in realtime mode * extract doFetch function * separate fetchPropBreakdown and fetchNextPage functions * subscribe for auto-updates in realtime * improve readability with function name changes
2023-01-02 18:42:57 +03:00
this.fetch = this.fetch.bind(this)
this.fetchAndReplace = this.fetchAndReplace.bind(this)
this.fetchAndConcat = this.fetchAndConcat.bind(this)
}
componentDidMount() {
window.addEventListener('resize', this.handleResize, false);
this.handleResize();
Realtime dashboard improvements (#2445) * add a new realtime-update-timer module * hook to the new 'tick' event in ListReport for auto-updates This commit fixes the bug where all reports using the `ListReport` component did not auto-update in realtime mode. Those reports are: - Pages (Top / Entry / Exit) - Locations (Countries / Regions / Cities) - Devices (Screen Sizes / Browsers + versions / OS-s + versions) * fetch data for ListReports only when scrolled into view * refactor fetching data in ListReport * refer to one source of truth for utm tags * make the 'All' tab in Sources auto-update * make all UTM tabs in Sources auto-update * fetch UTM data only when scrolled into view * auto-update Referrers with the new timer * auto-update google search terms * auto-update Conversions * make countries map auto-update * auto-update visitor-graph and top stats with new timer * use new tick event for current visitors (in Historical) * remove the old timer class * update changelog * Visual improvements to automatic realtime updates (#2532) * minor consistency fix for text color in dark mode * use FlipMove in goal conversions report * use FlipMove in ListReports * set main graph and top stats loading state correctly * refactor isIntervalValid function * enforce intervals are valid when set and stored * remove duplicate data fetching on interval change Fetching new data is handled by the `fetchGraphData` callback in `updateInterval` * refactor updateMetric function * make it clearer why 'metric' can be a faulty value * extract 'query' and 'site' variables from 'this.props' * reset interval state only when period is changed The 'maybeRollbackInterval' function was also used to fetch data. This commit replaces all those function calls with 'fetchGraphData' which better describes the actual behavior. We should only worry about rolling back the interval if 'query.period' has changed. This commit also stops the graph from flickering when it is updated in realtime. * update names of two variables * remove unnecessary negation * make collapsed graph state more explicit * consider stored invalid intervals when graph mounts * fix not showing loading spinner regression * remove interval state from VisitorGraph (#2540) * Realtime prop breakdown (#2535) * disable load more in realtime mode * extract doFetch function * separate fetchPropBreakdown and fetchNextPage functions * subscribe for auto-updates in realtime * improve readability with function name changes
2023-01-02 18:42:57 +03:00
this.fetchAndReplace()
if (this.props.query.period === 'realtime') {
document.addEventListener('tick', this.fetchAndReplace)
}
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize, false);
Realtime dashboard improvements (#2445) * add a new realtime-update-timer module * hook to the new 'tick' event in ListReport for auto-updates This commit fixes the bug where all reports using the `ListReport` component did not auto-update in realtime mode. Those reports are: - Pages (Top / Entry / Exit) - Locations (Countries / Regions / Cities) - Devices (Screen Sizes / Browsers + versions / OS-s + versions) * fetch data for ListReports only when scrolled into view * refactor fetching data in ListReport * refer to one source of truth for utm tags * make the 'All' tab in Sources auto-update * make all UTM tabs in Sources auto-update * fetch UTM data only when scrolled into view * auto-update Referrers with the new timer * auto-update google search terms * auto-update Conversions * make countries map auto-update * auto-update visitor-graph and top stats with new timer * use new tick event for current visitors (in Historical) * remove the old timer class * update changelog * Visual improvements to automatic realtime updates (#2532) * minor consistency fix for text color in dark mode * use FlipMove in goal conversions report * use FlipMove in ListReports * set main graph and top stats loading state correctly * refactor isIntervalValid function * enforce intervals are valid when set and stored * remove duplicate data fetching on interval change Fetching new data is handled by the `fetchGraphData` callback in `updateInterval` * refactor updateMetric function * make it clearer why 'metric' can be a faulty value * extract 'query' and 'site' variables from 'this.props' * reset interval state only when period is changed The 'maybeRollbackInterval' function was also used to fetch data. This commit replaces all those function calls with 'fetchGraphData' which better describes the actual behavior. We should only worry about rolling back the interval if 'query.period' has changed. This commit also stops the graph from flickering when it is updated in realtime. * update names of two variables * remove unnecessary negation * make collapsed graph state more explicit * consider stored invalid intervals when graph mounts * fix not showing loading spinner regression * remove interval state from VisitorGraph (#2540) * Realtime prop breakdown (#2535) * disable load more in realtime mode * extract doFetch function * separate fetchPropBreakdown and fetchNextPage functions * subscribe for auto-updates in realtime * improve readability with function name changes
2023-01-02 18:42:57 +03:00
document.removeEventListener('tick', this.fetchAndReplace)
}
handleResize() {
this.setState({ viewport: window.innerWidth });
}
getBarMaxWidth() {
const { viewport } = this.state;
return viewport > MOBILE_UPPER_WIDTH ? "16rem" : "10rem";
}
Realtime dashboard improvements (#2445) * add a new realtime-update-timer module * hook to the new 'tick' event in ListReport for auto-updates This commit fixes the bug where all reports using the `ListReport` component did not auto-update in realtime mode. Those reports are: - Pages (Top / Entry / Exit) - Locations (Countries / Regions / Cities) - Devices (Screen Sizes / Browsers + versions / OS-s + versions) * fetch data for ListReports only when scrolled into view * refactor fetching data in ListReport * refer to one source of truth for utm tags * make the 'All' tab in Sources auto-update * make all UTM tabs in Sources auto-update * fetch UTM data only when scrolled into view * auto-update Referrers with the new timer * auto-update google search terms * auto-update Conversions * make countries map auto-update * auto-update visitor-graph and top stats with new timer * use new tick event for current visitors (in Historical) * remove the old timer class * update changelog * Visual improvements to automatic realtime updates (#2532) * minor consistency fix for text color in dark mode * use FlipMove in goal conversions report * use FlipMove in ListReports * set main graph and top stats loading state correctly * refactor isIntervalValid function * enforce intervals are valid when set and stored * remove duplicate data fetching on interval change Fetching new data is handled by the `fetchGraphData` callback in `updateInterval` * refactor updateMetric function * make it clearer why 'metric' can be a faulty value * extract 'query' and 'site' variables from 'this.props' * reset interval state only when period is changed The 'maybeRollbackInterval' function was also used to fetch data. This commit replaces all those function calls with 'fetchGraphData' which better describes the actual behavior. We should only worry about rolling back the interval if 'query.period' has changed. This commit also stops the graph from flickering when it is updated in realtime. * update names of two variables * remove unnecessary negation * make collapsed graph state more explicit * consider stored invalid intervals when graph mounts * fix not showing loading spinner regression * remove interval state from VisitorGraph (#2540) * Realtime prop breakdown (#2535) * disable load more in realtime mode * extract doFetch function * separate fetchPropBreakdown and fetchNextPage functions * subscribe for auto-updates in realtime * improve readability with function name changes
2023-01-02 18:42:57 +03:00
fetch({concat}) {
if (!this.props.query.filters['goal']) return
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/property/${encodeURIComponent(this.state.propKey)}`, this.props.query, {limit: BREAKDOWN_LIMIT, page: this.state.page})
.then((res) => {
let breakdown = concat ? this.state.breakdown.concat(res) : res
this.setState(() => ({
loading: false,
breakdown: breakdown,
moreResultsAvailable: res.length >= BREAKDOWN_LIMIT
}))
})
}
fetchAndReplace() {
this.fetch({concat: false})
}
fetchAndConcat() {
this.fetch({concat: true})
}
loadMore() {
Realtime dashboard improvements (#2445) * add a new realtime-update-timer module * hook to the new 'tick' event in ListReport for auto-updates This commit fixes the bug where all reports using the `ListReport` component did not auto-update in realtime mode. Those reports are: - Pages (Top / Entry / Exit) - Locations (Countries / Regions / Cities) - Devices (Screen Sizes / Browsers + versions / OS-s + versions) * fetch data for ListReports only when scrolled into view * refactor fetching data in ListReport * refer to one source of truth for utm tags * make the 'All' tab in Sources auto-update * make all UTM tabs in Sources auto-update * fetch UTM data only when scrolled into view * auto-update Referrers with the new timer * auto-update google search terms * auto-update Conversions * make countries map auto-update * auto-update visitor-graph and top stats with new timer * use new tick event for current visitors (in Historical) * remove the old timer class * update changelog * Visual improvements to automatic realtime updates (#2532) * minor consistency fix for text color in dark mode * use FlipMove in goal conversions report * use FlipMove in ListReports * set main graph and top stats loading state correctly * refactor isIntervalValid function * enforce intervals are valid when set and stored * remove duplicate data fetching on interval change Fetching new data is handled by the `fetchGraphData` callback in `updateInterval` * refactor updateMetric function * make it clearer why 'metric' can be a faulty value * extract 'query' and 'site' variables from 'this.props' * reset interval state only when period is changed The 'maybeRollbackInterval' function was also used to fetch data. This commit replaces all those function calls with 'fetchGraphData' which better describes the actual behavior. We should only worry about rolling back the interval if 'query.period' has changed. This commit also stops the graph from flickering when it is updated in realtime. * update names of two variables * remove unnecessary negation * make collapsed graph state more explicit * consider stored invalid intervals when graph mounts * fix not showing loading spinner regression * remove interval state from VisitorGraph (#2540) * Realtime prop breakdown (#2535) * disable load more in realtime mode * extract doFetch function * separate fetchPropBreakdown and fetchNextPage functions * subscribe for auto-updates in realtime * improve readability with function name changes
2023-01-02 18:42:57 +03:00
this.setState({loading: true, page: this.state.page + 1}, this.fetchAndConcat.bind(this))
}
2020-11-04 14:35:05 +03:00
renderUrl(value) {
2021-08-24 15:23:01 +03:00
if (isValidHttpUrl(value.name)) {
2020-11-04 14:35:05 +03:00
return (
<a target="_blank" href={value.name} rel="noreferrer" className="hidden group-hover:block">
<svg className="inline h-4 w-4 ml-1 -mt-1 text-gray-600 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20"><path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"></path><path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"></path></svg>
2020-11-04 14:35:05 +03:00
</a>
)
}
return null
}
renderPropContent(value, query) {
return (
<span className="flex px-2 py-1.5 group dark:text-gray-300 relative z-9 break-all">
<Link
2021-08-24 15:23:01 +03:00
to={{pathname: window.location.pathname, search: query.toString()}}
className="md:truncate hover:underline block"
>
{ value.name }
</Link>
{ this.renderUrl(value) }
</span>
)
}
2020-10-30 11:49:41 +03:00
renderPropValue(value) {
const query = new URLSearchParams(window.location.search)
2020-10-30 11:49:41 +03:00
query.set('props', JSON.stringify({[this.state.propKey]: value.name}))
const { viewport } = this.state;
return (
<div className="flex items-center justify-between my-2" key={value.name}>
<Bar
count={value.unique_conversions}
plot="unique_conversions"
all={this.state.breakdown}
bg="bg-red-50 dark:bg-gray-500 dark:bg-opacity-15"
maxWidthDeduction={this.getBarMaxWidth()}
>
{this.renderPropContent(value, query)}
</Bar>
<div className="dark:text-gray-200">
<span className="font-medium inline-block w-20 text-right">{numberFormatter(value.unique_conversions)}</span>
{
viewport > MOBILE_UPPER_WIDTH ?
(
<span
className="font-medium inline-block w-20 text-right"
>{numberFormatter(value.total_conversions)}
</span>
)
: null
}
<span className="font-medium inline-block w-20 text-right">{numberFormatter(value.conversion_rate)}%</span>
</div>
</div>
)
}
2020-10-30 11:49:41 +03:00
changePropKey(newKey) {
storage.setItem(this.storageKey, newKey)
Realtime dashboard improvements (#2445) * add a new realtime-update-timer module * hook to the new 'tick' event in ListReport for auto-updates This commit fixes the bug where all reports using the `ListReport` component did not auto-update in realtime mode. Those reports are: - Pages (Top / Entry / Exit) - Locations (Countries / Regions / Cities) - Devices (Screen Sizes / Browsers + versions / OS-s + versions) * fetch data for ListReports only when scrolled into view * refactor fetching data in ListReport * refer to one source of truth for utm tags * make the 'All' tab in Sources auto-update * make all UTM tabs in Sources auto-update * fetch UTM data only when scrolled into view * auto-update Referrers with the new timer * auto-update google search terms * auto-update Conversions * make countries map auto-update * auto-update visitor-graph and top stats with new timer * use new tick event for current visitors (in Historical) * remove the old timer class * update changelog * Visual improvements to automatic realtime updates (#2532) * minor consistency fix for text color in dark mode * use FlipMove in goal conversions report * use FlipMove in ListReports * set main graph and top stats loading state correctly * refactor isIntervalValid function * enforce intervals are valid when set and stored * remove duplicate data fetching on interval change Fetching new data is handled by the `fetchGraphData` callback in `updateInterval` * refactor updateMetric function * make it clearer why 'metric' can be a faulty value * extract 'query' and 'site' variables from 'this.props' * reset interval state only when period is changed The 'maybeRollbackInterval' function was also used to fetch data. This commit replaces all those function calls with 'fetchGraphData' which better describes the actual behavior. We should only worry about rolling back the interval if 'query.period' has changed. This commit also stops the graph from flickering when it is updated in realtime. * update names of two variables * remove unnecessary negation * make collapsed graph state more explicit * consider stored invalid intervals when graph mounts * fix not showing loading spinner regression * remove interval state from VisitorGraph (#2540) * Realtime prop breakdown (#2535) * disable load more in realtime mode * extract doFetch function * separate fetchPropBreakdown and fetchNextPage functions * subscribe for auto-updates in realtime * improve readability with function name changes
2023-01-02 18:42:57 +03:00
this.setState({propKey: newKey, loading: true, breakdown: [], page: 1, moreResultsAvailable: false}, this.fetchAndReplace)
}
renderLoading() {
if (this.state.loading) {
return <div className="px-4 py-2"><div className="loading sm mx-auto"><div></div></div></div>
Realtime dashboard improvements (#2445) * add a new realtime-update-timer module * hook to the new 'tick' event in ListReport for auto-updates This commit fixes the bug where all reports using the `ListReport` component did not auto-update in realtime mode. Those reports are: - Pages (Top / Entry / Exit) - Locations (Countries / Regions / Cities) - Devices (Screen Sizes / Browsers + versions / OS-s + versions) * fetch data for ListReports only when scrolled into view * refactor fetching data in ListReport * refer to one source of truth for utm tags * make the 'All' tab in Sources auto-update * make all UTM tabs in Sources auto-update * fetch UTM data only when scrolled into view * auto-update Referrers with the new timer * auto-update google search terms * auto-update Conversions * make countries map auto-update * auto-update visitor-graph and top stats with new timer * use new tick event for current visitors (in Historical) * remove the old timer class * update changelog * Visual improvements to automatic realtime updates (#2532) * minor consistency fix for text color in dark mode * use FlipMove in goal conversions report * use FlipMove in ListReports * set main graph and top stats loading state correctly * refactor isIntervalValid function * enforce intervals are valid when set and stored * remove duplicate data fetching on interval change Fetching new data is handled by the `fetchGraphData` callback in `updateInterval` * refactor updateMetric function * make it clearer why 'metric' can be a faulty value * extract 'query' and 'site' variables from 'this.props' * reset interval state only when period is changed The 'maybeRollbackInterval' function was also used to fetch data. This commit replaces all those function calls with 'fetchGraphData' which better describes the actual behavior. We should only worry about rolling back the interval if 'query.period' has changed. This commit also stops the graph from flickering when it is updated in realtime. * update names of two variables * remove unnecessary negation * make collapsed graph state more explicit * consider stored invalid intervals when graph mounts * fix not showing loading spinner regression * remove interval state from VisitorGraph (#2540) * Realtime prop breakdown (#2535) * disable load more in realtime mode * extract doFetch function * separate fetchPropBreakdown and fetchNextPage functions * subscribe for auto-updates in realtime * improve readability with function name changes
2023-01-02 18:42:57 +03:00
} else if (this.state.moreResultsAvailable && this.props.query.period !== 'realtime') {
return (
<div className="w-full text-center my-4">
<button onClick={this.loadMore.bind(this)} type="button" className="button">
Load more
</button>
</div>
)
}
}
renderBody() {
return this.state.breakdown.map((propValue) => this.renderPropValue(propValue))
}
renderPill(key) {
2020-10-30 11:49:41 +03:00
const isActive = this.state.propKey === key
if (isActive) {
return <li key={key} className="inline-block h-5 text-indigo-700 dark:text-indigo-500 font-bold mr-2 active-prop-heading">{key}</li>
} else {
return <li key={key} className="hover:text-indigo-600 cursor-pointer mr-2" onClick={this.changePropKey.bind(this, key)}>{key}</li>
}
}
render() {
return (
<div className="w-full pl-3 sm:pl-6 mt-4">
<div className="flex-col sm:flex-row flex items-center pb-1">
<span className="text-xs font-bold text-gray-600 dark:text-gray-300 self-start sm:self-auto mb-1 sm:mb-0">Breakdown by:</span>
<ul className="flex flex-wrap font-medium text-xs text-gray-500 dark:text-gray-400 leading-5 pl-1 sm:pl-2">
2020-10-30 11:49:41 +03:00
{ this.props.goal.prop_names.map(this.renderPill.bind(this)) }
</ul>
</div>
{ this.renderBody() }
{ this.renderLoading()}
</div>
)
}
}