mirror of
https://github.com/plausible/analytics.git
synced 2024-12-25 02:24:55 +03:00
Merge branch 'feature/details-modal-mobile' of github.com:hirusi/analytics
This commit is contained in:
commit
87e9fb92d0
@ -31,7 +31,7 @@
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: rgba(0,0,0,0.6);
|
background: rgba(0,0,0,0.6);
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
overflow-x: hidden;
|
overflow-x: auto;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import { Link, withRouter } from 'react-router-dom'
|
|||||||
import Modal from './modal'
|
import Modal from './modal'
|
||||||
import * as api from '../../api'
|
import * as api from '../../api'
|
||||||
import numberFormatter from '../../number-formatter'
|
import numberFormatter from '../../number-formatter'
|
||||||
import Bar from '../bar'
|
|
||||||
import {parseQuery} from '../../query'
|
import {parseQuery} from '../../query'
|
||||||
|
|
||||||
class CountriesModal extends React.Component {
|
class CountriesModal extends React.Component {
|
||||||
@ -21,6 +20,10 @@ class CountriesModal extends React.Component {
|
|||||||
.then((res) => this.setState({loading: false, countries: res}))
|
.then((res) => this.setState({loading: false, countries: res}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label() {
|
||||||
|
return this.state.query.period === 'realtime' ? 'Current visitors' : 'Visitors'
|
||||||
|
}
|
||||||
|
|
||||||
renderCountry(country) {
|
renderCountry(country) {
|
||||||
const query = new URLSearchParams(window.location.search)
|
const query = new URLSearchParams(window.location.search)
|
||||||
query.set('country', country.name)
|
query.set('country', country.name)
|
||||||
@ -28,7 +31,11 @@ class CountriesModal extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<tr className="text-sm dark:text-gray-200" key={country.name}>
|
<tr className="text-sm dark:text-gray-200" key={country.name}>
|
||||||
<td className="p-2">
|
<td className="p-2">
|
||||||
<Link className="hover:underline" to={{search: query.toString(), pathname: '/' + encodeURIComponent(this.props.site.domain)}}>
|
<Link
|
||||||
|
className="hover:underline"
|
||||||
|
to={{search: query.toString(),
|
||||||
|
pathname: `/${ encodeURIComponent(this.props.site.domain)}`}}
|
||||||
|
>
|
||||||
{country.full_country_name}
|
{country.full_country_name}
|
||||||
</Link>
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
@ -39,27 +46,36 @@ class CountriesModal extends React.Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
label() {
|
|
||||||
return this.state.query.period === 'realtime' ? 'Current visitors' : 'Visitors'
|
|
||||||
}
|
|
||||||
|
|
||||||
renderBody() {
|
renderBody() {
|
||||||
if (this.state.loading) {
|
if (this.state.loading) {
|
||||||
return (
|
return (
|
||||||
<div className="loading mt-32 mx-auto"><div></div></div>
|
<div className="loading mt-32 mx-auto"><div></div></div>
|
||||||
)
|
)
|
||||||
} else if (this.state.countries) {
|
}
|
||||||
|
|
||||||
|
if (this.state.countries) {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<>
|
||||||
<h1 className="text-xl font-bold dark:text-gray-100">Top countries</h1>
|
<h1 className="text-xl font-bold dark:text-gray-100">Top countries</h1>
|
||||||
|
|
||||||
<div className="my-4 border-b border-gray-300 dark:border-gray-500"></div>
|
<div className="my-4 border-b border-gray-300 dark:border-gray-500"></div>
|
||||||
<main className="modal__content">
|
<main className="modal__content">
|
||||||
<table className="w-full table-striped table-fixed">
|
<table className="w-max overflow-x-auto md:w-full table-striped table-fixed">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Country</th>
|
<th
|
||||||
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">{this.label()}</th>
|
className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400"
|
||||||
|
align="left"
|
||||||
|
>
|
||||||
|
Country
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400"
|
||||||
|
align="right"
|
||||||
|
>
|
||||||
|
{this.label()}
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -67,7 +83,7 @@ class CountriesModal extends React.Component {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</main>
|
</main>
|
||||||
</React.Fragment>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link } from 'react-router-dom'
|
import { Link , withRouter } from 'react-router-dom'
|
||||||
import { withRouter } from 'react-router-dom'
|
|
||||||
|
|
||||||
import Modal from './modal'
|
import Modal from './modal'
|
||||||
import * as api from '../../api'
|
import * as api from '../../api'
|
||||||
@ -26,12 +26,23 @@ class EntryPagesModal extends React.Component {
|
|||||||
loadPages() {
|
loadPages() {
|
||||||
const {query, page, pages} = this.state;
|
const {query, page, pages} = this.state;
|
||||||
|
|
||||||
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/entry-pages`, query, {limit: 100, page})
|
api.get(
|
||||||
.then((res) => this.setState((state) => ({loading: false, pages: state.pages.concat(res), moreResultsAvailable: res.length === 100})))
|
`/api/stats/${encodeURIComponent(this.props.site.domain)}/entry-pages`,
|
||||||
|
query,
|
||||||
|
{limit: 100, page}
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
(res) => this.setState((state) => ({
|
||||||
|
loading: false,
|
||||||
|
pages: state.pages.concat(res),
|
||||||
|
moreResultsAvailable: res.length === 100
|
||||||
|
}))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadMore() {
|
loadMore() {
|
||||||
this.setState({loading: true, page: this.state.page + 1}, this.loadPages.bind(this))
|
const { page } = this.state;
|
||||||
|
this.setState({loading: true, page: page + 1}, this.loadPages.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
showVisitDuration() {
|
showVisitDuration() {
|
||||||
@ -40,10 +51,9 @@ class EntryPagesModal extends React.Component {
|
|||||||
|
|
||||||
formatBounceRate(page) {
|
formatBounceRate(page) {
|
||||||
if (typeof(page.bounce_rate) === 'number') {
|
if (typeof(page.bounce_rate) === 'number') {
|
||||||
return page.bounce_rate + '%'
|
return `${page.bounce_rate}%`;
|
||||||
} else {
|
|
||||||
return '-'
|
|
||||||
}
|
}
|
||||||
|
return '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPage(page) {
|
renderPage(page) {
|
||||||
@ -53,7 +63,15 @@ class EntryPagesModal extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<tr className="text-sm dark:text-gray-200" key={page.name}>
|
<tr className="text-sm dark:text-gray-200" key={page.name}>
|
||||||
<td className="p-2">
|
<td className="p-2">
|
||||||
<Link to={{pathname: `/${encodeURIComponent(this.props.site.domain)}`, search: query.toString()}} className="hover:underline">{page.name}</Link>
|
<Link
|
||||||
|
to={{
|
||||||
|
pathname: `/${encodeURIComponent(this.props.site.domain)}`,
|
||||||
|
search: query.toString()
|
||||||
|
}}
|
||||||
|
className="hover:underline"
|
||||||
|
>
|
||||||
|
{page.name}
|
||||||
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
<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.count)}</td>
|
||||||
<td className="p-2 w-32 font-medium" align="right">{numberFormatter(page.entries)}</td>
|
<td className="p-2 w-32 font-medium" align="right">{numberFormatter(page.entries)}</td>
|
||||||
@ -79,18 +97,34 @@ class EntryPagesModal extends React.Component {
|
|||||||
renderBody() {
|
renderBody() {
|
||||||
if (this.state.pages) {
|
if (this.state.pages) {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<>
|
||||||
<h1 className="text-xl font-bold dark:text-gray-100">Entry Pages</h1>
|
<h1 className="text-xl font-bold dark:text-gray-100">Entry Pages</h1>
|
||||||
|
|
||||||
<div className="my-4 border-b border-gray-300"></div>
|
<div className="my-4 border-b border-gray-300"></div>
|
||||||
<main className="modal__content">
|
<main className="modal__content">
|
||||||
<table className="w-full table-striped table-fixed">
|
<table className="w-max overflow-x-auto md:w-full table-striped table-fixed">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Page url</th>
|
<th
|
||||||
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Unique Entrances</th>
|
className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400"
|
||||||
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Total Entrances</th>
|
align="left"
|
||||||
{<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Visit Duration</th>}
|
>Page url
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400"
|
||||||
|
align="right"
|
||||||
|
>Unique Entrances
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400"
|
||||||
|
align="right"
|
||||||
|
>Total Entrances
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400"
|
||||||
|
align="right"
|
||||||
|
>Visit Duration
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -98,7 +132,7 @@ class EntryPagesModal extends React.Component {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</main>
|
</main>
|
||||||
</React.Fragment>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ class ExitPagesModal extends React.Component {
|
|||||||
|
|
||||||
<div className="my-4 border-b border-gray-300"></div>
|
<div className="my-4 border-b border-gray-300"></div>
|
||||||
<main className="modal__content">
|
<main className="modal__content">
|
||||||
<table className="w-full table-striped table-fixed">
|
<table className="w-max overflow-x-auto md:w-full table-striped table-fixed">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Page url</th>
|
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Page url</th>
|
||||||
|
@ -81,7 +81,7 @@ class GoogleKeywordsModal extends React.Component {
|
|||||||
}
|
}
|
||||||
} else if (this.state.searchTerms.length > 0) {
|
} else if (this.state.searchTerms.length > 0) {
|
||||||
return (
|
return (
|
||||||
<table className="w-full table-striped table-fixed">
|
<table className="w-max overflow-x-auto md:w-full table-striped table-fixed">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Search Term</th>
|
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Search Term</th>
|
||||||
|
@ -42,12 +42,32 @@ class Modal extends React.Component {
|
|||||||
this.props.history.push(`/${encodeURIComponent(this.props.site.domain)}${this.props.location.search}`)
|
this.props.history.push(`/${encodeURIComponent(this.props.site.domain)}${this.props.location.search}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
* Decide whether to set max-width, and if so, to what.
|
||||||
|
* If no max-width is available, set width instead to max-content.
|
||||||
|
*/
|
||||||
|
getStyle() {
|
||||||
|
const { maxWidth } = this.props;
|
||||||
|
const styleObject = {};
|
||||||
|
if (maxWidth) {
|
||||||
|
styleObject.maxWidth = maxWidth;
|
||||||
|
} else {
|
||||||
|
styleObject.width = "max-content";
|
||||||
|
}
|
||||||
|
return styleObject;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<div className="modal is-open" onClick={this.props.onClick}>
|
<div className="modal is-open" onClick={this.props.onClick}>
|
||||||
<div className="modal__overlay">
|
<div className="modal__overlay">
|
||||||
<button className="modal__close"></button>
|
<button className="modal__close"></button>
|
||||||
<div ref={this.node} className="modal__container dark:bg-gray-800" style={{maxWidth: this.props.maxWidth || '860px'}}>
|
<div
|
||||||
|
ref={this.node}
|
||||||
|
className="modal__container dark:bg-gray-800"
|
||||||
|
style={this.getStyle()}
|
||||||
|
>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ class PagesModal extends React.Component {
|
|||||||
|
|
||||||
<div className="my-4 border-b border-gray-300"></div>
|
<div className="my-4 border-b border-gray-300"></div>
|
||||||
<main className="modal__content">
|
<main className="modal__content">
|
||||||
<table className="w-full table-striped table-fixed">
|
<table className="w-max overflow-x-auto md:w-full table-striped table-fixed">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Page url</th>
|
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Page url</th>
|
||||||
|
@ -149,7 +149,7 @@ class ReferrerDrilldownModal extends React.Component {
|
|||||||
<h1 className="text-xl font-semibold mb-0 leading-none dark:text-gray-200">{this.state.totalVisitors} visitors from {decodeURIComponent(this.props.match.params.referrer)}<br /> {toHuman(this.state.query)}</h1>
|
<h1 className="text-xl font-semibold mb-0 leading-none dark:text-gray-200">{this.state.totalVisitors} visitors from {decodeURIComponent(this.props.match.params.referrer)}<br /> {toHuman(this.state.query)}</h1>
|
||||||
{this.renderGoalText()}
|
{this.renderGoalText()}
|
||||||
|
|
||||||
<table className="w-full table-striped table-fixed mt-4">
|
<table className="w-max overflow-x-auto md:w-full table-striped table-fixed mt-4">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Referrer</th>
|
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Referrer</th>
|
||||||
|
@ -125,7 +125,7 @@ class SourcesModal extends React.Component {
|
|||||||
<div className="my-4 border-b border-gray-300 dark:border-gray-500"></div>
|
<div className="my-4 border-b border-gray-300 dark:border-gray-500"></div>
|
||||||
|
|
||||||
<main className="modal__content">
|
<main className="modal__content">
|
||||||
<table className="w-full table-striped table-fixed">
|
<table className="w-max overflow-x-auto md:w-full table-striped table-fixed">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Source</th>
|
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Source</th>
|
||||||
|
Loading…
Reference in New Issue
Block a user