Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add conversion_rate to sources api and source table #1299

Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ All notable changes to this project will be documented in this file.
- Added `CLICKHOUSE_FLUSH_INTERVAL_MS` and `CLICKHOUSE_MAX_BUFFER_SIZE` configuration parameters plausible/analytics#1073
- Ability to invite users to sites with different roles plausible/analytics#1122
- Option to configure a custom name for the script file
- Add Conversion Rate to Top Sources, Top Pages Devices, Countries when filtered by a goal plausible/analytics#1299

### Fixed
- Fix weekly report time range plausible/analytics#951
Expand Down
20 changes: 14 additions & 6 deletions assets/js/dashboard/stats/devices/browsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default class Browsers extends React.Component {
this.fetchBrowsers()
}
}

onVisible() {
this.fetchBrowsers()
if (this.props.timer) this.props.timer.onTick(this.fetchBrowsers.bind(this))
Expand All @@ -37,6 +37,10 @@ export default class Browsers extends React.Component {
}
}

showConversionRate() {
return !!this.props.query.filters.goal
}

label() {
return this.props.query.period === 'realtime' ? 'Current visitors' : 'Visitors'
}
Expand All @@ -58,21 +62,22 @@ export default class Browsers extends React.Component {
} else {
query.set('browser', browser.name)
}
const maxWidthDeduction = this.showConversionRate() ? "10rem" : "5rem"

return (
<div className="flex items-center justify-between my-1 text-sm" key={browser.name}>
<Bar
count={browser.count}
all={this.state.browsers}
bg="bg-green-50 dark:bg-gray-500 dark:bg-opacity-15"
maxWidthDeduction="6rem"
maxWidthDeduction={maxWidthDeduction}
>
{this.renderBrowserContent(browser, query)}
</Bar>
<span className="font-medium dark:text-gray-200">
{numberFormatter(browser.count)}
<span className="inline-block w-8 text-xs text-right">({browser.percentage}%)</span>
<span className="font-medium dark:text-gray-200 text-right w-20">
{numberFormatter(browser.count)} <span className="inline-block w-8 text-xs"> ({browser.percentage}%)</span>
</span>
{this.showConversionRate() && <span className="font-medium dark:text-gray-200 w-20 text-right">{numberFormatter(browser.conversion_rate)}%</span>}
</div>
)
}
Expand All @@ -85,7 +90,10 @@ export default class Browsers extends React.Component {
<React.Fragment>
<div className="flex items-center justify-between mt-3 mb-2 text-xs font-bold tracking-wide text-gray-500 dark:text-gray-400">
<span>{ key }</span>
<span>{ this.label() }</span>
<div className="text-right">
<span className="inline-block w-20">{ this.label() }</span>
{this.showConversionRate() && <span className="inline-block w-20">CR</span>}
</div>
</div>
{ this.state.browsers && this.state.browsers.map(this.renderBrowser.bind(this)) }
</React.Fragment>
Expand Down
28 changes: 17 additions & 11 deletions assets/js/dashboard/stats/devices/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ class ScreenSizes extends React.Component {
if (this.props.timer) this.props.timer.onTick(this.fetchScreenSizes.bind(this))
}


fetchScreenSizes() {
api.get(
`/api/stats/${encodeURIComponent(this.props.site.domain)}/screen-sizes`,
Expand All @@ -66,21 +65,27 @@ class ScreenSizes extends React.Component {
.then((res) => this.setState({loading: false, sizes: res}))
}

showConversionRate() {
return !!this.props.query.filters.goal
}


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

renderScreenSize(size) {
const query = new URLSearchParams(window.location.search)
query.set('screen', size.name)
const maxWidthDeduction = this.showConversionRate() ? "10rem" : "5rem"

return (
<div className="flex items-center justify-between my-1 text-sm" key={size.name}>
<Bar
count={size.count}
all={this.state.sizes}
bg="bg-green-50 dark:bg-gray-500 dark:bg-opacity-15"
maxWidthDeduction="6rem"
maxWidthDeduction={maxWidthDeduction}
>
<span
tooltip={EXPLANATION[size.name]}
Expand All @@ -91,12 +96,10 @@ class ScreenSizes extends React.Component {
</Link>
</span>
</Bar>
<span
className="font-medium dark:text-gray-200"
>
{numberFormatter(size.count)}
<span className="inline-block w-8 text-xs text-right">({size.percentage}%)</span>
<span className="font-medium dark:text-gray-200 text-right w-20">
{numberFormatter(size.count)} <span className="inline-block w-8 text-xs text-right">({size.percentage}%)</span>
</span>
{this.showConversionRate() && <span className="font-medium dark:text-gray-200 w-20 text-right">{numberFormatter(size.conversion_rate)}%</span>}
</div>
)
}
Expand All @@ -109,7 +112,10 @@ class ScreenSizes extends React.Component {
className="flex items-center justify-between mt-3 mb-2 text-xs font-bold tracking-wide text-gray-500"
>
<span>Screen size</span>
<span>{ this.label() }</span>
<div className="text-right">
<span className="inline-block w-20">{ this.label() }</span>
{this.showConversionRate() && <span className="inline-block w-20">CR</span>}
</div>
</div>
{ this.state.sizes && this.state.sizes.map(this.renderScreenSize.bind(this)) }
</React.Fragment>
Expand Down Expand Up @@ -146,14 +152,14 @@ export default class Devices extends React.Component {
}
}


setMode(mode) {
return () => {
storage.setItem(this.tabKey, mode)
this.setState({mode})
}
}

renderContent() {
switch (this.state.mode) {
case 'browser':
Expand Down Expand Up @@ -190,7 +196,7 @@ export default class Devices extends React.Component {
</li>
)
}

return (
<li
className="cursor-pointer hover:text-indigo-600"
Expand Down
15 changes: 12 additions & 3 deletions assets/js/dashboard/stats/devices/operating-systems.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,18 @@ export default class OperatingSystems extends React.Component {
}
}

showConversionRate() {
return !!this.props.query.filters.goal
}

renderOperatingSystem(os) {
const query = new URLSearchParams(window.location.search)
if (this.props.query.filters.os) {
query.set('os_version', os.name)
} else {
query.set('os', os.name)
}
const maxWidthDeduction = this.showConversionRate() ? "10rem" : "5rem"

return (
<div
Expand All @@ -53,15 +58,16 @@ export default class OperatingSystems extends React.Component {
count={os.count}
all={this.state.operatingSystems}
bg="bg-green-50 dark:gray-500 dark:bg-opacity-15"
maxWidthDeduction="6rem"
maxWidthDeduction={maxWidthDeduction}
>
<span className="flex px-2 py-1.5 dark:text-gray-300 relative z-9 break-all">
<Link className="md:truncate block hover:underline" to={{search: query.toString()}}>
{os.name}
</Link>
</span>
</Bar>
<span className="font-medium dark:text-gray-200">{numberFormatter(os.count)} <span className="inline-block w-8 text-xs text-right">({os.percentage}%)</span></span>
<span className="font-medium dark:text-gray-200 text-right w-20">{numberFormatter(os.count)} <span className="inline-block w-8 text-xs text-right">({os.percentage}%)</span></span>
{this.showConversionRate() && <span className="font-medium dark:text-gray-200 w-20 text-right">{numberFormatter(os.conversion_rate)}%</span>}
</div>
)
}
Expand All @@ -78,7 +84,10 @@ export default class OperatingSystems extends React.Component {
<React.Fragment>
<div className="flex items-center justify-between mt-3 mb-2 text-xs font-bold tracking-wide text-gray-500 dark:text-gray-400">
<span>{ key }</span>
<span>{ this.label() }</span>
<div className="text-right">
<span className="inline-block w-20">{ this.label() }</span>
{this.showConversionRate() && <span className="inline-block w-20">CR</span>}
</div>
</div>
{ this.state.operatingSystems && this.state.operatingSystems.map(this.renderOperatingSystem.bind(this)) }
</React.Fragment>
Expand Down
8 changes: 7 additions & 1 deletion assets/js/dashboard/stats/modals/countries.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ class CountriesModal extends React.Component {
return this.state.query.period === 'realtime' ? 'Current visitors' : 'Visitors'
}

showConversionRate() {
return !!this.state.query.filters.goal
}

renderCountry(country) {
const query = new URLSearchParams(window.location.search)
query.set('country', country.name)
Expand All @@ -47,6 +51,7 @@ class CountriesModal extends React.Component {
<td className="p-2 w-32 font-medium" align="right">
{numberFormatter(country.count)} <span className="inline-block text-xs w-8 text-right">({country.percentage}%)</span>
</td>
{this.showConversionRate() && <td className="p-2 w-32 font-medium" align="right">{country.conversion_rate}%</td> }
</tr>
)
}
Expand All @@ -57,7 +62,7 @@ class CountriesModal extends React.Component {
<div className="loading mt-32 mx-auto"><div></div></div>
)
}

if (this.state.countries) {
return (
<>
Expand All @@ -81,6 +86,7 @@ class CountriesModal extends React.Component {
>
{this.label()}
</th>
{this.showConversionRate() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">CR</th>}
</tr>
</thead>
<tbody>
Expand Down
10 changes: 10 additions & 0 deletions assets/js/dashboard/stats/modals/entry-pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class EntryPagesModal extends React.Component {
return '-';
}

showConversionRate() {
return !!this.state.query.filters.goal
}

renderPage(page) {
const query = new URLSearchParams(window.location.search)
query.set('entry_page', page.name)
Expand All @@ -76,6 +80,7 @@ class EntryPagesModal extends React.Component {
<td className="p-2 w-32 font-medium" align="right">{numberFormatter(page.count)}</td>
<td className="p-2 w-32 font-medium" align="right">{numberFormatter(page.entries)}</td>
{this.showVisitDuration() && <td className="p-2 w-32 font-medium" align="right">{durationFormatter(page.visit_duration)}</td>}
{this.showConversionRate() && <td className="p-2 w-32 font-medium" align="right">{numberFormatter(page.conversion_rate)}%</td>}
</tr>
)
}
Expand Down Expand Up @@ -125,6 +130,11 @@ class EntryPagesModal extends React.Component {
align="right"
>Visit Duration
</th>
{this.showConversionRate() && <th
className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400"
align="right"
>CR
</th>}
</tr>
</thead>
<tbody>
Expand Down
6 changes: 6 additions & 0 deletions assets/js/dashboard/stats/modals/exit-pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class ExitPagesModal extends React.Component {
}
}

showConversionRate() {
return !!this.state.query.filters.goal
}

renderPage(page) {
const query = new URLSearchParams(window.location.search)
query.set('exit_page', page.name)
Expand All @@ -54,6 +58,7 @@ class ExitPagesModal extends React.Component {
<td className="p-2 w-32 font-medium" align="right">{numberFormatter(page.count)}</td>
<td className="p-2 w-32 font-medium" align="right">{numberFormatter(page.exits)}</td>
<td className="p-2 w-32 font-medium" align="right">{this.formatPercentage(page.exit_rate)}</td>
{this.showConversionRate() && <td className="p-2 w-32 font-medium" align="right">{numberFormatter(page.conversion_rate)}%</td>}
</tr>
)
}
Expand Down Expand Up @@ -87,6 +92,7 @@ class ExitPagesModal extends React.Component {
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Unique Exits</th>
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Total Exits</th>
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Exit Rate</th>
{this.showConversionRate() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">CR</th>}
</tr>
</thead>
<tbody>
Expand Down
6 changes: 6 additions & 0 deletions assets/js/dashboard/stats/modals/pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ class PagesModal extends React.Component {
return this.state.query.period !== 'realtime' && !(filters.goal || filters.source || filters.referrer)
}

showConversionRate() {
return !!this.state.query.filters.goal
}

formatBounceRate(page) {
if (typeof(page.bounce_rate) === 'number') {
return page.bounce_rate + '%'
Expand All @@ -66,6 +70,7 @@ class PagesModal extends React.Component {
{this.showPageviews() && <td className="p-2 w-32 font-medium" align="right">{numberFormatter(page.pageviews)}</td> }
{this.showExtra() && <td className="p-2 w-32 font-medium" align="right">{this.formatBounceRate(page)}</td> }
{this.showExtra() && <td className="p-2 w-32 font-medium" align="right">{timeOnPage}</td> }
{this.showConversionRate() && <td className="p-2 w-32 font-medium" align="right">{page.conversion_rate}%</td> }
</tr>
)
}
Expand Down Expand Up @@ -104,6 +109,7 @@ class PagesModal extends React.Component {
{this.showPageviews() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Pageviews</th>}
{this.showExtra() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Bounce rate</th>}
{this.showExtra() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Time on Page</th>}
{this.showConversionRate() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">CR</th>}
</tr>
</thead>
<tbody>
Expand Down
8 changes: 8 additions & 0 deletions assets/js/dashboard/stats/modals/sources.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ class SourcesModal extends React.Component {
return this.state.query.period !== 'realtime' && !this.state.query.filters.goal
}

showConversionRate() {
return !!this.state.query.filters.goal
}

loadMore() {
this.setState({loading: true, page: this.state.page + 1}, this.loadSources.bind(this))
}
Expand Down Expand Up @@ -82,6 +86,8 @@ class SourcesModal extends React.Component {
if (filter === 'utm_sources') query.set('utm_source', source.name)
if (filter === 'utm_campaigns') query.set('utm_campaign', source.name)

console.log(source)

return (
<tr className="text-sm dark:text-gray-200" key={source.name}>
<td className="p-2">
Expand All @@ -94,6 +100,7 @@ class SourcesModal extends React.Component {
<td className="p-2 w-32 font-medium" align="right">{numberFormatter(source.count)}</td>
{this.showExtra() && <td className="p-2 w-32 font-medium" align="right">{this.formatBounceRate(source)}</td> }
{this.showExtra() && <td className="p-2 w-32 font-medium" align="right">{this.formatDuration(source)}</td> }
{this.showConversionRate() && <td className="p-2 w-32 font-medium" align="right">{source.conversion_rate}%</td> }
</tr>
)
}
Expand Down Expand Up @@ -135,6 +142,7 @@ class SourcesModal extends React.Component {
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">{this.label()}</th>
{this.showExtra() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Bounce rate</th>}
{this.showExtra() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Visit duration</th>}
{this.showConversionRate() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">CR</th>}
</tr>
</thead>
<tbody>
Expand Down
Loading