)
}
@@ -241,6 +207,8 @@ export default function BreakdownModal({
setSearch(e.target.value)
}
+ const debouncedHandleInputChange = useDebounce(handleInputChange)
+
function renderSearchInput() {
return (
)
}
function renderModalBody() {
- if (results) {
+ if (data?.pages?.length) {
return (
-
{ reportInfo.title }
- { !initialLoading && loading && renderSmallLoadingSpinner() }
+ {reportInfo.title}
+ {!isPending && isFetching && renderSmallLoadingSpinner()}
- { searchEnabled && renderSearchInput()}
+ {searchEnabled && renderSearchInput()}
-
- { initialLoading && renderInitialLoadingSpinner() }
- { !initialLoading && renderModalBody() }
- { !loading && moreResultsAvailable && renderLoadMoreButton() }
+
+ {isPending && renderInitialLoadingSpinner()}
+ {!isPending && renderModalBody()}
+ {renderLoadMoreButton()}
)
diff --git a/assets/js/dashboard/stats/modals/conversions.js b/assets/js/dashboard/stats/modals/conversions.js
index 45e62bd0c..92aa5cd9d 100644
--- a/assets/js/dashboard/stats/modals/conversions.js
+++ b/assets/js/dashboard/stats/modals/conversions.js
@@ -3,15 +3,18 @@ import React, { useCallback, useState } from "react";
import Modal from './modal'
import BreakdownModal from "./breakdown-modal";
import * as metrics from "../reports/metrics";
+import * as url from '../../util/url';
+import { useSiteContext } from "../../site-context";
/*global BUILD_EXTRA*/
function ConversionsModal() {
const [showRevenue, setShowRevenue] = useState(false)
+ const site = useSiteContext();
const reportInfo = {
title: 'Goal Conversions',
dimension: 'goal',
- endpoint: '/conversions',
+ endpoint: url.apiPath(site, '/conversions'),
dimensionLabel: "Goal"
}
@@ -20,7 +23,7 @@ function ConversionsModal() {
prefix: reportInfo.dimension,
filter: ["is", reportInfo.dimension, [listItem.name]]
}
- }, [])
+ }, [reportInfo.dimension])
function chooseMetrics() {
return [
@@ -37,7 +40,7 @@ function ConversionsModal() {
// whether revenue metrics are passed into BreakdownModal in `metrics`.
const afterFetchData = useCallback((res) => {
setShowRevenue(revenueInResponse(res))
- }, [showRevenue])
+ }, [])
// After fetching the next page, we never want to set `showRevenue` to
// `false` as revenue metrics might exist in previously loaded data.
diff --git a/assets/js/dashboard/stats/modals/entry-pages.js b/assets/js/dashboard/stats/modals/entry-pages.js
index 5c6df5a48..f1d3068b3 100644
--- a/assets/js/dashboard/stats/modals/entry-pages.js
+++ b/assets/js/dashboard/stats/modals/entry-pages.js
@@ -4,15 +4,18 @@ import { hasGoalFilter, isRealTimeDashboard } from "../../util/filters";
import { addFilter } from '../../query'
import BreakdownModal from "./breakdown-modal";
import * as metrics from '../reports/metrics'
+import * as url from '../../util/url';
import { useQueryContext } from "../../query-context";
+import { useSiteContext } from "../../site-context";
function EntryPagesModal() {
const { query } = useQueryContext();
+ const site = useSiteContext();
const reportInfo = {
title: 'Entry Pages',
dimension: 'entry_page',
- endpoint: '/entry-pages',
+ endpoint: url.apiPath(site, '/entry-pages'),
dimensionLabel: 'Entry page'
}
@@ -21,11 +24,11 @@ function EntryPagesModal() {
prefix: reportInfo.dimension,
filter: ["is", reportInfo.dimension, [listItem.name]]
}
- }, [])
+ }, [reportInfo.dimension])
const addSearchFilter = useCallback((query, searchString) => {
return addFilter(query, ['contains', reportInfo.dimension, [searchString]])
- }, [])
+ }, [reportInfo.dimension])
function chooseMetrics() {
if (hasGoalFilter(query)) {
diff --git a/assets/js/dashboard/stats/modals/exit-pages.js b/assets/js/dashboard/stats/modals/exit-pages.js
index 1a4b97ca0..bd4bd2e5b 100644
--- a/assets/js/dashboard/stats/modals/exit-pages.js
+++ b/assets/js/dashboard/stats/modals/exit-pages.js
@@ -4,15 +4,18 @@ import { hasGoalFilter } from "../../util/filters";
import { addFilter } from '../../query'
import BreakdownModal from "./breakdown-modal";
import * as metrics from '../reports/metrics'
+import * as url from '../../util/url';
import { useQueryContext } from "../../query-context";
+import { useSiteContext } from "../../site-context";
function ExitPagesModal() {
const { query } = useQueryContext();
+ const site = useSiteContext();
const reportInfo = {
title: 'Exit Pages',
dimension: 'exit_page',
- endpoint: '/exit-pages',
+ endpoint: url.apiPath(site, '/exit-pages'),
dimensionLabel: 'Page url'
}
@@ -21,11 +24,11 @@ function ExitPagesModal() {
prefix: reportInfo.dimension,
filter: ["is", reportInfo.dimension, [listItem.name]]
}
- }, [])
+ }, [reportInfo.dimension])
const addSearchFilter = useCallback((query, searchString) => {
return addFilter(query, ['contains', reportInfo.dimension, [searchString]])
- }, [])
+ }, [reportInfo.dimension])
function chooseMetrics() {
if (hasGoalFilter(query)) {
diff --git a/assets/js/dashboard/stats/modals/locations-modal.js b/assets/js/dashboard/stats/modals/locations-modal.js
index d13e17f4a..75373924c 100644
--- a/assets/js/dashboard/stats/modals/locations-modal.js
+++ b/assets/js/dashboard/stats/modals/locations-modal.js
@@ -5,7 +5,9 @@ import Modal from './modal'
import { hasGoalFilter } from "../../util/filters";
import BreakdownModal from "./breakdown-modal";
import * as metrics from "../reports/metrics";
+import * as url from '../../util/url';
import { useQueryContext } from "../../query-context";
+import { useSiteContext } from "../../site-context";
const VIEWS = {
countries: { title: 'Top Countries', dimension: 'country', endpoint: '/countries', dimensionLabel: 'Country' },
@@ -15,18 +17,20 @@ const VIEWS = {
function LocationsModal({ location }) {
const { query } = useQueryContext();
+ const site = useSiteContext();
const urlParts = location.pathname.split('/')
const currentView = urlParts[urlParts.length - 1]
- const reportInfo = VIEWS[currentView]
+ let reportInfo = VIEWS[currentView]
+ reportInfo.endpoint = url.apiPath(site, reportInfo.endpoint)
const getFilterInfo = useCallback((listItem) => {
return {
prefix: reportInfo.dimension,
filter: ["is", reportInfo.dimension, [listItem.code]]
}
- }, [])
+ }, [reportInfo.dimension])
function chooseMetrics() {
if (hasGoalFilter(query)) {
diff --git a/assets/js/dashboard/stats/modals/pages.js b/assets/js/dashboard/stats/modals/pages.js
index 9b62d0664..5d59e0d07 100644
--- a/assets/js/dashboard/stats/modals/pages.js
+++ b/assets/js/dashboard/stats/modals/pages.js
@@ -4,15 +4,18 @@ import { hasGoalFilter, isRealTimeDashboard } from "../../util/filters";
import { addFilter } from '../../query'
import BreakdownModal from "./breakdown-modal";
import * as metrics from '../reports/metrics'
+import * as url from '../../util/url';
import { useQueryContext } from "../../query-context";
+import { useSiteContext } from "../../site-context";
function PagesModal() {
const { query } = useQueryContext();
+ const site = useSiteContext();
const reportInfo = {
title: 'Top Pages',
dimension: 'page',
- endpoint: '/pages',
+ endpoint: url.apiPath(site, '/pages'),
dimensionLabel: 'Page url'
}
@@ -21,11 +24,11 @@ function PagesModal() {
prefix: reportInfo.dimension,
filter: ["is", reportInfo.dimension, [listItem.name]]
}
- }, [])
+ }, [reportInfo.dimension])
const addSearchFilter = useCallback((query, searchString) => {
return addFilter(query, ['contains', reportInfo.dimension, [searchString]])
- }, [])
+ }, [reportInfo.dimension])
function chooseMetrics() {
if (hasGoalFilter(query)) {
diff --git a/assets/js/dashboard/stats/modals/props.js b/assets/js/dashboard/stats/modals/props.js
index 339d5faeb..cafa9e200 100644
--- a/assets/js/dashboard/stats/modals/props.js
+++ b/assets/js/dashboard/stats/modals/props.js
@@ -7,6 +7,7 @@ import { specialTitleWhenGoalFilter } from "../behaviours/goal-conversions";
import { EVENT_PROPS_PREFIX, hasGoalFilter } from "../../util/filters"
import BreakdownModal from "./breakdown-modal";
import * as metrics from "../reports/metrics";
+import * as url from "../../util/url";
import { revenueAvailable } from "../../query";
import { useQueryContext } from "../../query-context";
import { useSiteContext } from "../../site-context";
@@ -22,7 +23,7 @@ function PropsModal({ location }) {
const reportInfo = {
title: specialTitleWhenGoalFilter(query, 'Custom Property Breakdown'),
dimension: propKey,
- endpoint: `/custom-prop-values/${propKey}`,
+ endpoint: url.apiPath(site, `/custom-prop-values/${propKey}`),
dimensionLabel: propKey
}
@@ -31,11 +32,11 @@ function PropsModal({ location }) {
prefix: `${EVENT_PROPS_PREFIX}${propKey}`,
filter: ["is", `${EVENT_PROPS_PREFIX}${propKey}`, [listItem.name]]
}
- }, [])
+ }, [propKey])
const addSearchFilter = useCallback((query, searchString) => {
return addFilter(query, ['contains', `${EVENT_PROPS_PREFIX}${propKey}`, [searchString]])
- }, [])
+ }, [propKey])
function chooseMetrics() {
return [
diff --git a/assets/js/dashboard/stats/modals/referrer-drilldown.js b/assets/js/dashboard/stats/modals/referrer-drilldown.js
index 977ca7fab..9bfb52fd9 100644
--- a/assets/js/dashboard/stats/modals/referrer-drilldown.js
+++ b/assets/js/dashboard/stats/modals/referrer-drilldown.js
@@ -5,16 +5,19 @@ import Modal from './modal'
import { hasGoalFilter, isRealTimeDashboard } from "../../util/filters";
import BreakdownModal from "./breakdown-modal";
import * as metrics from "../reports/metrics";
+import * as url from "../../util/url";
import { addFilter } from "../../query";
import { useQueryContext } from "../../query-context";
+import { useSiteContext } from "../../site-context";
function ReferrerDrilldownModal({ match }) {
const { query } = useQueryContext();
+ const site = useSiteContext();
const reportInfo = {
title: "Referrer Drilldown",
dimension: 'referrer',
- endpoint: `/referrers/${match.params.referrer}`,
+ endpoint: url.apiPath(site, `/referrers/${match.params.referrer}`),
dimensionLabel: "Referrer"
}
@@ -23,11 +26,11 @@ function ReferrerDrilldownModal({ match }) {
prefix: reportInfo.dimension,
filter: ['is', reportInfo.dimension, [listItem.name]]
}
- }, [])
+ }, [reportInfo.dimension])
const addSearchFilter = useCallback((query, searchString) => {
return addFilter(query, ['contains', reportInfo.dimension, [searchString]])
- }, [])
+ }, [reportInfo.dimension])
function chooseMetrics() {
if (hasGoalFilter(query)) {
diff --git a/assets/js/dashboard/stats/modals/sources.js b/assets/js/dashboard/stats/modals/sources.js
index ee6fdf173..85b4f1bce 100644
--- a/assets/js/dashboard/stats/modals/sources.js
+++ b/assets/js/dashboard/stats/modals/sources.js
@@ -5,8 +5,10 @@ import Modal from './modal'
import { hasGoalFilter, isRealTimeDashboard } from "../../util/filters";
import BreakdownModal from "./breakdown-modal";
import * as metrics from "../reports/metrics";
+import * as url from "../../util/url";
import { addFilter } from "../../query";
import { useQueryContext } from "../../query-context";
+import { useSiteContext } from "../../site-context";
const VIEWS = {
sources: {
@@ -39,22 +41,24 @@ const VIEWS = {
function SourcesModal({ location }) {
const { query } = useQueryContext();
+ const site = useSiteContext();
const urlParts = location.pathname.split('/')
const currentView = urlParts[urlParts.length - 1]
- const reportInfo = VIEWS[currentView].info
+ let reportInfo = VIEWS[currentView].info
+ reportInfo.endpoint = url.apiPath(site, reportInfo.endpoint)
const getFilterInfo = useCallback((listItem) => {
return {
prefix: reportInfo.dimension,
filter: ["is", reportInfo.dimension, [listItem.name]]
}
- }, [])
+ }, [reportInfo.dimension])
const addSearchFilter = useCallback((query, searchString) => {
return addFilter(query, ['contains', reportInfo.dimension, [searchString]])
- }, [])
+ }, [reportInfo.dimension])
function chooseMetrics() {
if (hasGoalFilter(query)) {
diff --git a/assets/package-lock.json b/assets/package-lock.json
index 6eebcc2e5..70f8dea1f 100644
--- a/assets/package-lock.json
+++ b/assets/package-lock.json
@@ -17,6 +17,7 @@
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.6",
"@tailwindcss/typography": "^0.4.1",
+ "@tanstack/react-query": "^5.51.1",
"abortcontroller-polyfill": "^1.7.3",
"alpinejs": "^3.13.1",
"chart.js": "^3.3.2",
@@ -446,6 +447,30 @@
"tailwindcss": ">=2.0.0"
}
},
+ "node_modules/@tanstack/query-core": {
+ "version": "5.51.1",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.51.1.tgz",
+ "integrity": "sha512-fJBMQMpo8/KSsWW5ratJR5+IFr7YNJ3K2kfP9l5XObYHsgfVy1w3FJUWU4FT2fj7+JMaEg33zOcNDBo0LMwHnw==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.51.1",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.51.1.tgz",
+ "integrity": "sha512-s47HKFnQ4HOJAHoIiXcpna/roMMPZJPy6fJ6p4ZNVn8+/onlLBEDd1+xc8OnDuwgvecqkZD7Z2mnSRbcWefrKw==",
+ "dependencies": {
+ "@tanstack/query-core": "5.51.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0"
+ }
+ },
"node_modules/@types/d3": {
"version": "3.5.38",
"license": "MIT"
diff --git a/assets/package.json b/assets/package.json
index 536561284..b770e4592 100644
--- a/assets/package.json
+++ b/assets/package.json
@@ -17,6 +17,7 @@
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.6",
"@tailwindcss/typography": "^0.4.1",
+ "@tanstack/react-query": "^5.51.1",
"abortcontroller-polyfill": "^1.7.3",
"alpinejs": "^3.13.1",
"chart.js": "^3.3.2",