import React, { Fragment, useState, useEffect, useCallback } from 'react' import { Menu, Transition } from '@headlessui/react' import { ChevronDownIcon } from '@heroicons/react/20/solid' import classNames from 'classnames' 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 { hasGoalFilter } from '../../util/filters' import { useSiteContext } from '../../site-context' import { useQueryContext } from '../../query-context' import { useUserContext } from '../../user-context' /*global BUILD_EXTRA*/ /*global require*/ function maybeRequire() { if (BUILD_EXTRA) { return require('../../extra/funnel') } else { return { default: null } } } const Funnel = maybeRequire().default const ACTIVE_CLASS = 'inline-block h-5 text-indigo-700 dark:text-indigo-500 font-bold active-prop-heading truncate text-left' const DEFAULT_CLASS = 'hover:text-indigo-600 cursor-pointer truncate text-left' 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', '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 [funnelNames, _setFunnelNames] = useState(site.funnels.map(({ name }) => name)) 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) } }, []) useEffect(() => { const justRemovedGoalFilter = !hasGoalFilter(query) if (mode === PROPS && justRemovedGoalFilter && showingPropsForGoalFilter) { setShowingPropsForGoalFilter(false) setMode(CONVERSIONS) } }, [hasGoalFilter(query)]) useEffect(() => { setMode(defaultMode()) }, [enabledModes]) useEffect(() => setLoading(true), [query, mode]) function disableMode(mode) { setEnabledModes(enabledModes.filter((m) => { return m !== mode })) } function setFunnel(selectedFunnel) { return () => { storage.setItem(tabKey, FUNNELS) storage.setItem(funnelKey, selectedFunnel) setMode(FUNNELS) setSelectedFunnel(selectedFunnel) } } 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 hasFunnels() { return site.funnels.length > 0 && site.funnelsAvailable } function tabFunnelPicker() { return
Funnels
{funnelNames.map((funnelName) => { return ( {({ active }) => ( {funnelName} )} ) })}
} function tabSwitcher(toMode, displayName) { const className = classNames({ [ACTIVE_CLASS]: mode == toMode, [DEFAULT_CLASS]: mode !== toMode }) const setTab = () => { storage.setItem(tabKey, toMode) setMode(toMode) } return (
{displayName}
) } function tabs() { return (
{isEnabled(CONVERSIONS) && tabSwitcher(CONVERSIONS, 'Goals')} {isEnabled(PROPS) && tabSwitcher(PROPS, 'Properties')} {isEnabled(FUNNELS) && Funnel && (hasFunnels() ? tabFunnelPicker() : tabSwitcher(FUNNELS, 'Funnels'))}
) } 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 (

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

{renderImportedQueryUnsupportedWarning()}
{tabs()}
{renderContent()}
) } else { return null } }