import React, { useState, useEffect, useCallback } from 'react' import * as storage from '../../util/storage' import ImportedQueryUnsupportedWarning from '../imported-query-unsupported-warning' import GoalConversions, { specialTitleWhenGoalFilter, SPECIAL_GOALS } from './goal-conversions' import Properties from './props' import { FeatureSetupNotice } from '../../components/notice' import { hasConversionGoalFilter } from '../../util/filters' import { useSiteContext } from '../../site-context' import { useQueryContext } from '../../query-context' import { useUserContext } from '../../user-context' import { DropdownTabButton, TabButton, TabWrapper } from '../../components/tabs' /*global BUILD_EXTRA*/ /*global require*/ function maybeRequire() { if (BUILD_EXTRA) { // eslint-disable-next-line @typescript-eslint/no-require-imports return require('../../extra/funnel') } else { return { default: null } } } const Funnel = maybeRequire().default export const CONVERSIONS = 'conversions' export const PROPS = 'props' export const FUNNELS = 'funnels' export const sectionTitles = { [CONVERSIONS]: 'Goal Conversions', [PROPS]: 'Custom Properties', [FUNNELS]: 'Funnels' } export default function Behaviours({ importedDataInView }) { const { query } = useQueryContext() const site = useSiteContext() const user = useUserContext() const adminAccess = ['owner', 'admin', 'editor', 'super_admin'].includes( user.role ) const tabKey = storage.getDomainScopedStorageKey('behavioursTab', site.domain) const funnelKey = storage.getDomainScopedStorageKey( 'behavioursTabFunnel', site.domain ) const [enabledModes, setEnabledModes] = useState(getEnabledModes()) const [mode, setMode] = useState(defaultMode()) const [loading, setLoading] = useState(true) const [selectedFunnel, setSelectedFunnel] = useState(defaultSelectedFunnel()) const [showingPropsForGoalFilter, setShowingPropsForGoalFilter] = useState(false) const [skipImportedReason, setSkipImportedReason] = useState(null) const onGoalFilterClick = useCallback((e) => { const goalName = e.target.innerHTML const isSpecialGoal = Object.keys(SPECIAL_GOALS).includes(goalName) const isPageviewGoal = goalName.startsWith('Visit ') if ( !isSpecialGoal && !isPageviewGoal && enabledModes.includes(PROPS) && site.hasProps ) { setShowingPropsForGoalFilter(true) setMode(PROPS) } // eslint-disable-next-line react-hooks/exhaustive-deps }, []) useEffect(() => { const justRemovedGoalFilter = !hasConversionGoalFilter(query) if (mode === PROPS && justRemovedGoalFilter && showingPropsForGoalFilter) { setShowingPropsForGoalFilter(false) setMode(CONVERSIONS) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [hasConversionGoalFilter(query)]) useEffect(() => { setMode(defaultMode()) // eslint-disable-next-line react-hooks/exhaustive-deps }, [enabledModes]) useEffect(() => setLoading(true), [query, mode]) function disableMode(mode) { setEnabledModes( enabledModes.filter((m) => { return m !== mode }) ) } function setFunnelFactory(selectedFunnelName) { return () => { storage.setItem(tabKey, FUNNELS) storage.setItem(funnelKey, selectedFunnelName) setMode(FUNNELS) setSelectedFunnel(selectedFunnelName) } } function defaultSelectedFunnel() { const stored = storage.getItem(funnelKey) const storedExists = stored && site.funnels.some((f) => f.name === stored) if (storedExists) { return stored } else if (site.funnels.length > 0) { const firstAvailable = site.funnels[0].name storage.setItem(funnelKey, firstAvailable) return firstAvailable } } function setTabFactory(tab) { return () => { storage.setItem(tabKey, tab) setMode(tab) } } function afterFetchData(apiResponse) { setLoading(false) setSkipImportedReason(apiResponse.skip_imported_reason) } function renderConversions() { if (site.hasGoals) { return ( ) } else if (adminAccess) { return ( ) } else { return noDataYet() } } function renderFunnels() { if (Funnel === null) { return featureUnavailable() } else if (Funnel && selectedFunnel && site.funnelsAvailable) { return } else if (Funnel && adminAccess) { let callToAction if (site.funnelsAvailable) { callToAction = { action: 'Set up funnels', link: `/${encodeURIComponent(site.domain)}/settings/funnels` } } else { callToAction = { action: 'Upgrade', link: '/billing/choose-plan' } } return ( ) } else { return noDataYet() } } function renderProps() { if (site.hasProps && site.propsAvailable) { return } else if (adminAccess) { let callToAction if (site.propsAvailable) { callToAction = { action: 'Set up props', link: `/${encodeURIComponent(site.domain)}/settings/properties` } } else { callToAction = { action: 'Upgrade', link: '/billing/choose-plan' } } return ( ) } else { return noDataYet() } } function noDataYet() { return (
No data yet
) } function featureUnavailable() { return (
This feature is unavailable
) } function onHideAction(mode) { return () => { disableMode(mode) } } function renderContent() { switch (mode) { case CONVERSIONS: return renderConversions() case PROPS: return renderProps() case FUNNELS: return renderFunnels() } } function defaultMode() { if (enabledModes.length === 0) { return null } const storedMode = storage.getItem(tabKey) if (storedMode && enabledModes.includes(storedMode)) { return storedMode } if (enabledModes.includes(CONVERSIONS)) { return CONVERSIONS } if (enabledModes.includes(PROPS)) { return PROPS } return FUNNELS } function getEnabledModes() { let enabledModes = [] for (const feature of Object.keys(sectionTitles)) { const isOptedOut = site[feature + 'OptedOut'] const isAvailable = site[feature + 'Available'] !== false // If the feature is not supported by the site owner's subscription, // it only makes sense to display the feature tab to the owner itself // as only they can upgrade to make the feature available. const callToActionIsMissing = !isAvailable && user.role !== 'owner' if (!isOptedOut && !callToActionIsMissing) { enabledModes.push(feature) } } return enabledModes } function isEnabled(mode) { return enabledModes.includes(mode) } function isRealtime() { return query.period === 'realtime' } function sectionTitle() { if (mode === CONVERSIONS) { return specialTitleWhenGoalFilter(query, sectionTitles[mode]) } else { return sectionTitles[mode] } } function renderImportedQueryUnsupportedWarning() { if (mode === CONVERSIONS) { return ( ) } else if (mode === PROPS) { return ( ) } else { return ( ) } } if (!mode) { return null } return (

{sectionTitle() + (isRealtime() ? ' (last 30min)' : '')}

{renderImportedQueryUnsupportedWarning()}
{isEnabled(CONVERSIONS) && ( Goals )} {isEnabled(PROPS) && ( Properties )} {isEnabled(FUNNELS) && Funnel && (site.funnels.length > 0 && site.funnelsAvailable ? ( ({ label: name, onClick: setFunnelFactory(name), selected: mode === FUNNELS && selectedFunnel === name }))} collectionTitle="Funnels" searchable={true} > Funnels ) : ( Funnels ))}
{renderContent()}
) }