diff --git a/assets/js/dashboard/hooks/use-previous.ts b/assets/js/dashboard/hooks/use-previous.ts new file mode 100644 index 000000000000..84de4c36c623 --- /dev/null +++ b/assets/js/dashboard/hooks/use-previous.ts @@ -0,0 +1,15 @@ +import { useRef, useEffect } from 'react'; + +function usePrevious(value: T): T | undefined { + const ref = useRef(undefined); + + useEffect(() => { + // Update the ref with the current value after render + ref.current = value; + }, [value]); + + // Return the previous value + return ref.current; +} + +export default usePrevious; diff --git a/assets/js/dashboard/stats/locations/index.js b/assets/js/dashboard/stats/locations/index.js index 6bb42f260f94..886ed245de71 100644 --- a/assets/js/dashboard/stats/locations/index.js +++ b/assets/js/dashboard/stats/locations/index.js @@ -257,4 +257,4 @@ function LocationsWithContext() { const site = useSiteContext(); return } -export default LocationsWithContext \ No newline at end of file +export default LocationsWithContext diff --git a/assets/js/dashboard/stats/sources/source-list.js b/assets/js/dashboard/stats/sources/source-list.js index 78e4dbc2a7a4..24c810be269a 100644 --- a/assets/js/dashboard/stats/sources/source-list.js +++ b/assets/js/dashboard/stats/sources/source-list.js @@ -3,9 +3,10 @@ import React, { Fragment, useEffect, useState } from 'react'; import * as storage from '../../util/storage'; import * as url from '../../util/url'; import * as api from '../../api'; +import usePrevious from '../../hooks/use-previous'; import ListReport from '../reports/list'; import * as metrics from '../reports/metrics'; -import { hasGoalFilter } from "../../util/filters"; +import { getFiltersByKeyPrefix, hasGoalFilter } from "../../util/filters"; import { Menu, Transition } from '@headlessui/react'; import { ChevronDownIcon } from '@heroicons/react/20/solid'; import classNames from 'classnames'; @@ -15,11 +16,11 @@ import { useSiteContext } from '../../site-context'; import { sourcesRoute, channelsRoute, utmCampaignsRoute, utmContentsRoute, utmMediumsRoute, utmSourcesRoute, utmTermsRoute } from '../../router'; const UTM_TAGS = { - utm_medium: { label: 'UTM Medium', shortLabel: 'UTM Medium', endpoint: '/utm_mediums' }, - utm_source: { label: 'UTM Source', shortLabel: 'UTM Source', endpoint: '/utm_sources' }, - utm_campaign: { label: 'UTM Campaign', shortLabel: 'UTM Campai', endpoint: '/utm_campaigns' }, - utm_content: { label: 'UTM Content', shortLabel: 'UTM Conten', endpoint: '/utm_contents' }, - utm_term: { label: 'UTM Term', shortLabel: 'UTM Term', endpoint: '/utm_terms' }, + utm_medium: { title: 'UTM Mediums', label: 'Medium', endpoint: '/utm_mediums' }, + utm_source: { title: 'UTM Sources', label: 'Source', endpoint: '/utm_sources' }, + utm_campaign: { title: 'UTM Campaigns', label: 'Campaign', endpoint: '/utm_campaigns' }, + utm_content: { title: 'UTM Contents', label: 'Content', endpoint: '/utm_contents' }, + utm_term: { title: 'UTM Terms', label: 'Term', endpoint: '/utm_terms' }, } function AllSources({ afterFetchData }) { @@ -67,7 +68,7 @@ function AllSources({ afterFetchData }) { ) } -function Channels({ afterFetchData }) { +function Channels({ onClick, afterFetchData }) { const { query } = useQueryContext(); const site = useSiteContext(); @@ -95,6 +96,7 @@ function Channels({ afterFetchData }) { afterFetchData={afterFetchData} getFilterFor={getFilterFor} keyLabel="Channel" + onClick={onClick} metrics={chooseMetrics()} detailsLinkProps={{ path: channelsRoute.path, search: (search) => search }} color="bg-blue-50" @@ -146,6 +148,15 @@ function UTMSources({ tab, afterFetchData }) { ) } +const labelFor = { + 'channels': 'Top Channels', + 'all': 'Top Sources' +} + +for (const [key, utm_tag] of Object.entries(UTM_TAGS)) { + labelFor[key] = utm_tag.title +} + export default function SourceList() { const site = useSiteContext(); const { query } = useQueryContext(); @@ -154,9 +165,23 @@ export default function SourceList() { const [currentTab, setCurrentTab] = useState(storedTab || 'all') const [loading, setLoading] = useState(true) const [skipImportedReason, setSkipImportedReason] = useState(null) + const previousQuery = usePrevious(query); useEffect(() => setLoading(true), [query, currentTab]) + useEffect(() => { + const isRemovingFilter = (filterName) => { + if (!previousQuery) return false + + return getFiltersByKeyPrefix(previousQuery, filterName).length > 0 && + getFiltersByKeyPrefix(query, filterName).length == 0 + } + + if (currentTab == 'all' && isRemovingFilter('channel')) { + setTab('channels')() + } + }, [query, currentTab]) + function setTab(tab) { return () => { storage.setItem(tabKey, tab) @@ -168,14 +193,14 @@ export default function SourceList() { const activeClass = 'inline-block h-5 text-indigo-700 dark:text-indigo-500 font-bold active-prop-heading truncate text-left' const defaultClass = 'hover:text-indigo-600 cursor-pointer truncate text-left' const dropdownOptions = Object.keys(UTM_TAGS) - let buttonText = UTM_TAGS[currentTab] ? UTM_TAGS[currentTab].label : 'Campaigns' + let buttonText = UTM_TAGS[currentTab] ? UTM_TAGS[currentTab].title : 'Campaigns' return (
-
All
{site.flags.channels &&
Channels
} +
Sources
@@ -208,7 +233,7 @@ export default function SourceList() { currentTab === option ? 'font-bold' : '' )} > - {UTM_TAGS[option].label} + {UTM_TAGS[option].title} )} @@ -222,11 +247,15 @@ export default function SourceList() { ) } + function onChannelClick() { + setTab('all')() + } + function renderContent() { if (currentTab === 'all') { return } else if (currentTab == 'channels') { - return + return } else { return } @@ -243,7 +272,7 @@ export default function SourceList() {

- Top Sources + {labelFor[currentTab]}