diff --git a/assets/js/dashboard/realtime.js b/assets/js/dashboard/realtime.js
index 5f3a3e912f49..c07b66878d70 100644
--- a/assets/js/dashboard/realtime.js
+++ b/assets/js/dashboard/realtime.js
@@ -5,7 +5,7 @@ import SiteSwitcher from './site-switcher'
import Filters from './filters'
import CurrentVisitors from './stats/current-visitors'
import VisitorGraph from './stats/visitor-graph'
-import Referrers from './stats/referrers'
+import Sources from './stats/sources'
import Pages from './stats/pages'
import Countries from './stats/countries'
import Devices from './stats/devices'
@@ -34,7 +34,7 @@ export default class Stats extends React.Component {
diff --git a/assets/js/dashboard/stats/sources/index.js b/assets/js/dashboard/stats/sources/index.js
new file mode 100644
index 000000000000..716f62689c96
--- /dev/null
+++ b/assets/js/dashboard/stats/sources/index.js
@@ -0,0 +1,14 @@
+import React from 'react';
+import SearchTerms from './search-terms'
+import SourceList from './source-list'
+import ReferrerList from './referrer-list'
+
+export default function Sources(props) {
+ if (props.query.filters.source === 'Google') {
+ return
+ } else if (props.query.filters.source) {
+ return
+ } else {
+ return
+ }
+}
diff --git a/assets/js/dashboard/stats/referrers.js b/assets/js/dashboard/stats/sources/referrer-list.js
similarity index 80%
rename from assets/js/dashboard/stats/referrers.js
rename to assets/js/dashboard/stats/sources/referrer-list.js
index 39bd61b49935..9fc520b8501b 100644
--- a/assets/js/dashboard/stats/referrers.js
+++ b/assets/js/dashboard/stats/sources/referrer-list.js
@@ -2,11 +2,11 @@ import React from 'react';
import { Link } from 'react-router-dom'
import FlipMove from 'react-flip-move';
-import FadeIn from '../fade-in'
-import Bar from './bar'
-import MoreLink from './more-link'
-import numberFormatter from '../number-formatter'
-import * as api from '../api'
+import FadeIn from '../../fade-in'
+import Bar from '../bar'
+import MoreLink from '../more-link'
+import numberFormatter from '../../number-formatter'
+import * as api from '../../api'
function LinkOption(props) {
if (props.disabled) {
@@ -66,19 +66,14 @@ export default class Referrers extends React.Component {
renderReferrer(referrer) {
const query = new URLSearchParams(window.location.search)
-
- if (this.props.query.filters.source) {
- query.set('referrer', referrer.name)
- } else {
- query.set('source', referrer.name)
- }
+ query.set('referrer', referrer.name)
return (
-
+
{ referrer.name }
@@ -96,15 +91,11 @@ export default class Referrers extends React.Component {
renderList() {
if (this.state.referrers.length > 0) {
- const source = this.props.query.filters.source
- const keyLabel = source === 'Google' ? 'Search term' : source ? 'Referrer' : 'Source'
- const valLabel = this.props.query.period === 'realtime' ? 'Active visitors' : 'Visitors'
-
return (
- { keyLabel }
- { valLabel }
+ Referrer
+ { this.label() }
@@ -118,16 +109,12 @@ export default class Referrers extends React.Component {
}
renderContent() {
- const source = this.props.query.filters.source
- const title = source === 'Google' ? 'Search terms' : source ? 'Top Referrers' : 'Top Sources'
- const endpoint = source ? 'referrers/' + encodeURIComponent(source) : 'referrers'
-
if (this.state.referrers) {
return (
- {title}
+ Top Referrers
{ this.renderList() }
-
+
)
}
diff --git a/assets/js/dashboard/stats/sources/search-terms.js b/assets/js/dashboard/stats/sources/search-terms.js
new file mode 100644
index 000000000000..1f73fd53dafa
--- /dev/null
+++ b/assets/js/dashboard/stats/sources/search-terms.js
@@ -0,0 +1,119 @@
+import React from 'react';
+import FadeIn from '../../fade-in'
+import Bar from '../bar'
+import MoreLink from '../more-link'
+import numberFormatter from '../../number-formatter'
+import RocketIcon from '../modals/rocket-icon'
+import * as api from '../../api'
+
+export default class SearchTerms extends React.Component {
+ constructor(props) {
+ super(props)
+ this.state = {loading: true}
+ }
+
+ componentDidMount() {
+ this.fetchSearchTerms()
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.query !== prevProps.query) {
+ this.setState({loading: true, terms: null})
+ this.fetchSearchTerms()
+ }
+ }
+
+ fetchSearchTerms() {
+ api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/referrers/Google`, this.props.query)
+ .then((res) => this.setState({
+ loading: false,
+ searchTerms: res.search_terms || [],
+ notConfigured: res.not_configured,
+ isOwner: res.is_owner
+ }))
+ }
+
+ renderSearchTerm(term) {
+ return (
+
+
+
+
+
+
+ { term.name }
+
+
+
+
{numberFormatter(term.count)}
+
+ )
+ }
+
+ renderList() {
+ if (this.props.query.filters.goal) {
+ return (
+
+
+
Sorry, we cannot show which keywords converted best for goal {this.props.query.filters.goal}
+
Google does not share this information
+
+ )
+
+ } else if (this.state.notConfigured) {
+ return (
+
+
+
The site is not connected to Google Search Keywords
+
Cannot show search terms
+ {this.state.isOwner &&
Connect with Google }
+
+ )
+ } else if (this.state.searchTerms.length > 0) {
+ const valLabel = this.props.query.period === 'realtime' ? 'Active visitors' : 'Visitors'
+
+ return (
+
+
+ Search term
+ {valLabel}
+
+
+ {this.state.searchTerms.map(this.renderSearchTerm.bind(this))}
+
+ )
+ } else {
+ return (
+
+
+
Could not find any search terms for this period
+
Google Search Console data is sampled and delayed by 24-36h
+
+
+ )
+ }
+ }
+
+ renderContent() {
+ if (this.state.searchTerms) {
+ return (
+
+ Search Terms
+ { this.renderList() }
+
+
+ )
+ }
+ }
+
+ render() {
+ return (
+
+ { this.state.loading &&
}
+
+ { this.renderContent() }
+
+
+ )
+ }
+}
diff --git a/assets/js/dashboard/stats/sources/source-list.js b/assets/js/dashboard/stats/sources/source-list.js
new file mode 100644
index 000000000000..0c7d33929c2a
--- /dev/null
+++ b/assets/js/dashboard/stats/sources/source-list.js
@@ -0,0 +1,108 @@
+import React from 'react';
+import { Link } from 'react-router-dom'
+import FlipMove from 'react-flip-move';
+
+import FadeIn from '../../fade-in'
+import Bar from '../bar'
+import MoreLink from '../more-link'
+import numberFormatter from '../../number-formatter'
+import * as api from '../../api'
+
+export default class Referrers extends React.Component {
+ constructor(props) {
+ super(props)
+ this.state = {loading: true}
+ }
+
+ componentDidMount() {
+ this.fetchReferrers()
+ if (this.props.timer) this.props.timer.onTick(this.fetchReferrers.bind(this))
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.query !== prevProps.query) {
+ this.setState({loading: true, referrers: null})
+ this.fetchReferrers()
+ }
+ }
+
+ showNoRef() {
+ return this.props.query.period === 'realtime'
+ }
+
+ fetchReferrers() {
+ if (this.props.query.filters.goal) {
+ api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/goal/referrers`, this.props.query)
+ .then((res) => this.setState({loading: false, referrers: res}))
+ } else {
+ api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/referrers`, this.props.query, {show_noref: this.showNoRef()})
+ .then((res) => this.setState({loading: false, referrers: res}))
+ }
+ }
+
+ renderReferrer(referrer) {
+ const query = new URLSearchParams(window.location.search)
+ query.set('source', referrer.name)
+
+ return (
+
+
+
+
+
+
+ { referrer.name }
+
+
+
+
{numberFormatter(referrer.count)}
+
+ )
+ }
+
+ label() {
+ return this.props.query.period === 'realtime' ? 'Active visitors' : 'Visitors'
+ }
+
+ renderList() {
+ if (this.state.referrers.length > 0) {
+ return (
+
+
+ Source
+ {this.label()}
+
+
+
+ {this.state.referrers.map(this.renderReferrer.bind(this))}
+
+
+ )
+ } else {
+ return No data yet
+ }
+ }
+
+ renderContent() {
+ if (this.state.referrers) {
+ return (
+
+ Top sources
+ { this.renderList() }
+
+
+ )
+ }
+ }
+
+ render() {
+ return (
+
+ { this.state.loading &&
}
+
+ { this.renderContent() }
+
+
+ )
+ }
+}