Skip to content
Merged
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
4 changes: 2 additions & 2 deletions assets/js/dashboard/historical.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -35,7 +35,7 @@ export default class Historical extends React.Component {
<Filters query={this.props.query} history={this.props.history} />
<VisitorGraph site={this.props.site} query={this.props.query} />
<div className="w-full block md:flex items-start justify-between">
<Referrers site={this.props.site} query={this.props.query} />
<Sources site={this.props.site} query={this.props.query} />
<Pages site={this.props.site} query={this.props.query} />
</div>
<div className="w-full block md:flex items-start justify-between">
Expand Down
4 changes: 2 additions & 2 deletions assets/js/dashboard/realtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -34,7 +34,7 @@ export default class Stats extends React.Component {
<Filters query={this.props.query} history={this.props.history} />
<VisitorGraph site={this.props.site} query={this.props.query} timer={this.props.timer} />
<div className="w-full block md:flex items-start justify-between">
<Referrers site={this.props.site} query={this.props.query} timer={this.props.timer} />
<Sources site={this.props.site} query={this.props.query} timer={this.props.timer} />
<Pages site={this.props.site} query={this.props.query} timer={this.props.timer} />
</div>
<div className="w-full block md:flex items-start justify-between">
Expand Down
14 changes: 14 additions & 0 deletions assets/js/dashboard/stats/sources/index.js
Original file line number Diff line number Diff line change
@@ -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 <SearchTerms {...props} />
} else if (props.query.filters.source) {
return <ReferrerList {...props} />
} else {
return <SourceList {...props} />
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 (
<div className="flex items-center justify-between my-1 text-sm" key={referrer.name}>
<div className="w-full h-8" style={{maxWidth: 'calc(100% - 4rem)'}}>
<Bar count={referrer.count} all={this.state.referrers} bg="bg-blue-50" />
<span className="flex px-2" style={{marginTop: '-26px'}} >
<LinkOption className="block truncate" to={{search: query.toString()}} disabled={this.props.query.filters.goal || this.props.query.filters.source === 'Google' || referrer.name === 'Direct / None'}>
<LinkOption className="block truncate" to={{search: query.toString()}} disabled={referrer.name === 'Direct / None'}>
<img src={`https://icons.duckduckgo.com/ip3/${referrer.url}.ico`} className="inline h-4 w-4 mr-2 align-middle -mt-px" />
{ referrer.name }
</LinkOption>
Expand All @@ -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 (
<React.Fragment>
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 text-xs font-bold tracking-wide">
<span>{ keyLabel }</span>
<span>{ valLabel }</span>
<span>Referrer</span>
<span>{ this.label() }</span>
</div>

<FlipMove>
Expand All @@ -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 (
<React.Fragment>
<h3 className="font-bold">{title}</h3>
<h3 className="font-bold">Top Referrers</h3>
{ this.renderList() }
<MoreLink site={this.props.site} list={this.state.referrers} endpoint={endpoint} />
<MoreLink site={this.props.site} list={this.state.referrers} endpoint={`referrers/${this.props.query.filters.source}`} />
</React.Fragment>
)
}
Expand Down
119 changes: 119 additions & 0 deletions assets/js/dashboard/stats/sources/search-terms.js
Original file line number Diff line number Diff line change
@@ -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 (
<div className="flex items-center justify-between my-1 text-sm" key={term.name}>
<div className="w-full h-8" style={{maxWidth: 'calc(100% - 4rem)'}}>
<Bar count={term.count} all={this.state.searchTerms} bg="bg-blue-50" />
<span className="flex px-2" style={{marginTop: '-26px'}} >
<span className="block truncate">
<img src={`https://icons.duckduckgo.com/ip3/${term.url}.ico`} className="inline h-4 w-4 mr-2 align-middle -mt-px" />
{ term.name }
</span>
</span>
</div>
<span className="font-medium">{numberFormatter(term.count)}</span>
</div>
)
}

renderList() {
if (this.props.query.filters.goal) {
return (
<div className="text-center text-gray-700 text-sm mt-20">
<RocketIcon />
<div>Sorry, we cannot show which keywords converted best for goal <b>{this.props.query.filters.goal}</b></div>
<div>Google does not share this information</div>
</div>
)

} else if (this.state.notConfigured) {
return (
<div className="text-center text-gray-700 text-sm mt-20">
<RocketIcon />
<div>The site is not connected to Google Search Keywords</div>
<div>Cannot show search terms</div>
{this.state.isOwner && <a href={`/${encodeURIComponent(this.props.site.domain)}/settings#google-auth`} className="button mt-4">Connect with Google</a> }
</div>
)
} else if (this.state.searchTerms.length > 0) {
const valLabel = this.props.query.period === 'realtime' ? 'Active visitors' : 'Visitors'

return (
<React.Fragment>
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 text-xs font-bold tracking-wide">
<span>Search term</span>
<span>{valLabel}</span>
</div>

{this.state.searchTerms.map(this.renderSearchTerm.bind(this))}
</React.Fragment>
)
} else {
return (
<div className="text-center text-gray-700 text-sm mt-20">
<RocketIcon />
<div>Could not find any search terms for this period</div>
<div>Google Search Console data is sampled and delayed by 24-36h</div>
<div>Read more on <a href="https://docs.plausible.io/google-search-console-integration/#i-dont-see-google-search-query-data-in-my-dashboard" target="_blank" className="hover:underline text-indigo-700">our documentation</a></div>
</div>
)
}
}

renderContent() {
if (this.state.searchTerms) {
return (
<React.Fragment>
<h3 className="font-bold">Search Terms</h3>
{ this.renderList() }
<MoreLink site={this.props.site} list={this.state.searchTerms} endpoint="referrers/Google" />
</React.Fragment>
)
}
}

render() {
return (
<div className="stats-item relative bg-white shadow-xl rounded p-4" style={{height: '436px'}}>
{ this.state.loading && <div className="loading mt-44 mx-auto"><div></div></div> }
<FadeIn show={!this.state.loading}>
{ this.renderContent() }
</FadeIn>
</div>
)
}
}
108 changes: 108 additions & 0 deletions assets/js/dashboard/stats/sources/source-list.js
Original file line number Diff line number Diff line change
@@ -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 (
<div className="flex items-center justify-between my-1 text-sm" key={referrer.name}>
<div className="w-full h-8" style={{maxWidth: 'calc(100% - 4rem)'}}>
<Bar count={referrer.count} all={this.state.referrers} bg="bg-blue-50" />
<span className="flex px-2" style={{marginTop: '-26px'}} >
<Link className="block truncate hover:underline" to={{search: query.toString()}}>
<img src={`https://icons.duckduckgo.com/ip3/${referrer.url}.ico`} className="inline h-4 w-4 mr-2 align-middle -mt-px" />
{ referrer.name }
</Link>
</span>
</div>
<span className="font-medium">{numberFormatter(referrer.count)}</span>
</div>
)
}

label() {
return this.props.query.period === 'realtime' ? 'Active visitors' : 'Visitors'
}

renderList() {
if (this.state.referrers.length > 0) {
return (
<React.Fragment>
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 text-xs font-bold tracking-wide">
<span>Source</span>
<span>{this.label()}</span>
</div>

<FlipMove>
{this.state.referrers.map(this.renderReferrer.bind(this))}
</FlipMove>
</React.Fragment>
)
} else {
return <div className="text-center mt-44 font-medium text-gray-500">No data yet</div>
}
}

renderContent() {
if (this.state.referrers) {
return (
<React.Fragment>
<h3 className="font-bold">Top sources</h3>
{ this.renderList() }
<MoreLink site={this.props.site} list={this.state.referrers} endpoint="referrers" />
</React.Fragment>
)
}
}

render() {
return (
<div className="stats-item relative bg-white shadow-xl rounded p-4" style={{height: '436px'}}>
{ this.state.loading && <div className="loading mt-44 mx-auto"><div></div></div> }
<FadeIn show={!this.state.loading}>
{ this.renderContent() }
</FadeIn>
</div>
)
}
}