analytics/assets/js/dashboard/stats/pages/index.js

229 lines
6.3 KiB
JavaScript

import React, { useEffect, useState } from 'react'
import * as storage from '../../util/storage'
import * as url from '../../util/url'
import * as api from '../../api'
import ListReport from './../reports/list'
import * as metrics from './../reports/metrics'
import ImportedQueryUnsupportedWarning from '../imported-query-unsupported-warning'
import { hasConversionGoalFilter } from '../../util/filters'
import { useQueryContext } from '../../query-context'
import { useSiteContext } from '../../site-context'
import { entryPagesRoute, exitPagesRoute, topPagesRoute } from '../../router'
import { TabButton, TabWrapper } from '../../components/tabs'
function EntryPages({ afterFetchData }) {
const { query } = useQueryContext()
const site = useSiteContext()
function fetchData() {
return api.get(url.apiPath(site, '/entry-pages'), query, { limit: 9 })
}
function getExternalLinkUrl(page) {
return url.externalLinkForPage(site, page.name)
}
function getFilterInfo(listItem) {
return {
prefix: 'entry_page',
filter: ['is', 'entry_page', [listItem['name']]]
}
}
function chooseMetrics() {
return [
metrics.createVisitors({
defaultLabel: 'Unique entrances',
width: 'w-36',
meta: { plot: true }
}),
!hasConversionGoalFilter(query) &&
metrics.createPercentage({ meta: { showOnHover: true } }),
hasConversionGoalFilter(query) && metrics.createConversionRate()
].filter((metric) => !!metric)
}
return (
<ListReport
fetchData={fetchData}
afterFetchData={afterFetchData}
getFilterInfo={getFilterInfo}
keyLabel="Entry page"
metrics={chooseMetrics()}
detailsLinkProps={{
path: entryPagesRoute.path,
search: (search) => search
}}
getExternalLinkUrl={getExternalLinkUrl}
color="bg-orange-50 group-hover/row:bg-orange-100"
/>
)
}
function ExitPages({ afterFetchData }) {
const site = useSiteContext()
const { query } = useQueryContext()
function fetchData() {
return api.get(url.apiPath(site, '/exit-pages'), query, { limit: 9 })
}
function getExternalLinkUrl(page) {
return url.externalLinkForPage(site, page.name)
}
function getFilterInfo(listItem) {
return {
prefix: 'exit_page',
filter: ['is', 'exit_page', [listItem['name']]]
}
}
function chooseMetrics() {
return [
metrics.createVisitors({
defaultLabel: 'Unique exits',
width: 'w-36',
meta: { plot: true }
}),
!hasConversionGoalFilter(query) &&
metrics.createPercentage({ meta: { showOnHover: true } }),
hasConversionGoalFilter(query) && metrics.createConversionRate()
].filter((metric) => !!metric)
}
return (
<ListReport
fetchData={fetchData}
afterFetchData={afterFetchData}
getFilterInfo={getFilterInfo}
keyLabel="Exit page"
metrics={chooseMetrics()}
detailsLinkProps={{
path: exitPagesRoute.path,
search: (search) => search
}}
getExternalLinkUrl={getExternalLinkUrl}
color="bg-orange-50 group-hover/row:bg-orange-100"
/>
)
}
function TopPages({ afterFetchData }) {
const { query } = useQueryContext()
const site = useSiteContext()
function fetchData() {
return api.get(url.apiPath(site, '/pages'), query, { limit: 9 })
}
function getExternalLinkUrl(page) {
return url.externalLinkForPage(site, page.name)
}
function getFilterInfo(listItem) {
return {
prefix: 'page',
filter: ['is', 'page', [listItem['name']]]
}
}
function chooseMetrics() {
return [
metrics.createVisitors({ meta: { plot: true } }),
!hasConversionGoalFilter(query) &&
metrics.createPercentage({ meta: { showOnHover: true } }),
hasConversionGoalFilter(query) && metrics.createConversionRate()
].filter((metric) => !!metric)
}
return (
<ListReport
fetchData={fetchData}
afterFetchData={afterFetchData}
getFilterInfo={getFilterInfo}
keyLabel="Page"
metrics={chooseMetrics()}
detailsLinkProps={{
path: topPagesRoute.path,
search: (search) => search
}}
getExternalLinkUrl={getExternalLinkUrl}
color="bg-orange-50 group-hover/row:bg-orange-100"
/>
)
}
const labelFor = {
pages: 'Top pages',
'entry-pages': 'Entry pages',
'exit-pages': 'Exit pages'
}
export default function Pages() {
const { query } = useQueryContext()
const site = useSiteContext()
const tabKey = `pageTab__${site.domain}`
const storedTab = storage.getItem(tabKey)
const [mode, setMode] = useState(storedTab || 'pages')
const [loading, setLoading] = useState(true)
const [skipImportedReason, setSkipImportedReason] = useState(null)
function switchTab(mode) {
storage.setItem(tabKey, mode)
setMode(mode)
}
function afterFetchData(apiResponse) {
setLoading(false)
setSkipImportedReason(apiResponse.skip_imported_reason)
}
useEffect(() => setLoading(true), [query, mode])
function renderContent() {
switch (mode) {
case 'entry-pages':
return <EntryPages afterFetchData={afterFetchData} />
case 'exit-pages':
return <ExitPages afterFetchData={afterFetchData} />
case 'pages':
default:
return <TopPages afterFetchData={afterFetchData} />
}
}
return (
<div className="group/report overflow-x-hidden">
{/* Header Container */}
<div className="w-full flex justify-between">
<div className="flex gap-x-1">
<h3 className="font-bold dark:text-gray-100">
{labelFor[mode] || 'Page Visits'}
</h3>
<ImportedQueryUnsupportedWarning
loading={loading}
skipImportedReason={skipImportedReason}
/>
</div>
<TabWrapper>
{[
{ label: 'Top pages', value: 'pages' },
{ label: 'Entry pages', value: 'entry-pages' },
{ label: 'Exit pages', value: 'exit-pages' }
].map(({ value, label }) => (
<TabButton
active={mode === value}
onClick={() => switchTab(value)}
key={value}
>
{label}
</TabButton>
))}
</TabWrapper>
</div>
{/* Main Contents */}
{renderContent()}
</div>
)
}