From 82822a735bf7b0045036ba845b5de821faf8ed77 Mon Sep 17 00:00:00 2001 From: Jaanus Sellin Date: Thu, 27 Jun 2024 12:48:57 +0300 Subject: [PATCH] feat: command bar track events (#7469) Start tracking plausible events 1. Log the search keywords that returned 0 results 2. Track all clicks, based on source(search/recents/pages), type etc. --- .../src/component/commandBar/CommandBar.tsx | 32 ++++++++++++--- .../component/commandBar/CommandFeatures.tsx | 11 +++++- .../component/commandBar/PageSuggestions.tsx | 24 +++++++++++- .../RecentlyVisited/CommandResultGroup.tsx | 16 ++++++++ .../RecentlyVisited/RecentlyVisited.tsx | 39 +++++++++++++++++++ frontend/src/hooks/usePlausibleTracker.ts | 3 +- 6 files changed, 117 insertions(+), 8 deletions(-) diff --git a/frontend/src/component/commandBar/CommandBar.tsx b/frontend/src/component/commandBar/CommandBar.tsx index 4eb69b8125..13d78e4285 100644 --- a/frontend/src/component/commandBar/CommandBar.tsx +++ b/frontend/src/component/commandBar/CommandBar.tsx @@ -25,6 +25,7 @@ import { useRoutes } from 'component/layout/MainLayout/NavigationSidebar/useRout import { useAsyncDebounce } from 'react-table'; import useProjects from 'hooks/api/getters/useProjects/useProjects'; import { CommandFeatures } from './CommandFeatures'; +import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; export const CommandResultsPaper = styled(Paper)(({ theme }) => ({ position: 'absolute', @@ -95,6 +96,7 @@ interface IPageRouteInfo { } export const CommandBar = () => { + const { trackEvent } = usePlausibleTracker(); const searchInputRef = useRef(null); const searchContainerRef = useRef(null); const [showSuggestions, setShowSuggestions] = useState(false); @@ -105,6 +107,8 @@ export const CommandBar = () => { const [searchedPages, setSearchedPages] = useState< CommandResultGroupItem[] >([]); + const [searchedFlagCount, setSearchedFlagCount] = useState(0); + const [value, setValue] = useState(''); const { lastVisited } = useRecentlyVisited(); const { routes } = useRoutes(); const allRoutes: Record = {}; @@ -148,10 +152,25 @@ export const CommandBar = () => { link: page.path, })); setSearchedPages(mappedPages); + + const noResultsFound = + query.length !== 0 && + mappedProjects.length === 0 && + mappedPages.length === 0 && + searchedFlagCount === 0; + if (noResultsFound) { + trackEvent('command-bar', { + props: { + eventType: 'no search results found', + query: query, + }, + }); + } }, 200); const onSearchChange = (value: string) => { debouncedSetSearchState(value); + setValue(value); }; const hotkey = useKeyboardShortcut( @@ -194,7 +213,7 @@ export const CommandBar = () => { 'aria-label': placeholder, 'data-testid': SEARCH_INPUT, }} - value={searchString} + value={value} onChange={(e) => onSearchChange(e.target.value)} onFocus={() => { setShowSuggestions(true); @@ -203,13 +222,13 @@ export const CommandBar = () => { theme.spacing(4) }}> { - e.stopPropagation(); // prevent outside click from the lazily added element + e.stopPropagation(); onSearchChange(''); searchInputRef.current?.focus(); }} @@ -226,11 +245,14 @@ export const CommandBar = () => { {searchString !== undefined && ( - + )} void; } -export const CommandFeatures = ({ searchString }: ICommandBar) => { +export const CommandFeatures = ({ + searchString, + setSearchedFlagCount, +}: ICommandBar) => { const { features = [] } = useFeatureSearch( { query: searchString, @@ -24,6 +29,10 @@ export const CommandFeatures = ({ searchString }: ICommandBar) => { description: feature.description, })); + useEffect(() => { + setSearchedFlagCount(flags.length); + }, [JSON.stringify(flags)]); + return ( ); diff --git a/frontend/src/component/commandBar/PageSuggestions.tsx b/frontend/src/component/commandBar/PageSuggestions.tsx index a31917addb..7047b9b7e9 100644 --- a/frontend/src/component/commandBar/PageSuggestions.tsx +++ b/frontend/src/component/commandBar/PageSuggestions.tsx @@ -9,6 +9,8 @@ import { import { Link } from 'react-router-dom'; import { IconRenderer } from 'component/layout/MainLayout/NavigationSidebar/IconRenderer'; import type { Theme } from '@mui/material/styles/createTheme'; +import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; +import type { JSX } from 'react'; const listItemButtonStyle = (theme: Theme) => ({ border: `1px solid transparent`, @@ -42,10 +44,16 @@ const StyledListItemText = styled(ListItemText)(({ theme }) => ({ fontSize: theme.fontSizes.smallBody, })); +interface IPageSuggestionItem { + icon: JSX.Element; + name: string; + path: string; +} + const toListItemData = ( items: string[], routes: Record, -) => { +): IPageSuggestionItem[] => { return items.map((item) => { return { name: routes[item]?.title ?? item, @@ -71,8 +79,19 @@ export const PageSuggestions = ({ }: { routes: Record; }) => { + const { trackEvent } = usePlausibleTracker(); const filtered = pages.filter((page) => routes[page]); const pageItems = toListItemData(filtered, routes); + const onClick = (item: IPageSuggestionItem) => { + trackEvent('command-bar', { + props: { + eventType: `click`, + source: 'suggestions', + eventTarget: 'Pages', + pageType: item.name, + }, + }); + }; return ( Pages @@ -83,6 +102,9 @@ export const PageSuggestions = ({ dense={true} component={Link} to={item.path} + onClick={() => { + onClick(item); + }} sx={listItemButtonStyle} > ({ borderRadius: theme.spacing(0.5), @@ -52,11 +53,23 @@ export const CommandResultGroup = ({ groupName, items, }: CommandResultGroupProps) => { + const { trackEvent } = usePlausibleTracker(); const slicedItems = items.slice(0, 3); if (items.length === 0) { return null; } + + const onClick = (item: CommandResultGroupItem) => { + trackEvent('command-bar', { + props: { + eventType: `click`, + source: 'search', + eventTarget: groupName, + ...(groupName === 'Pages' && { pageType: item.name }), + }, + }); + }; return ( <> @@ -68,6 +81,9 @@ export const CommandResultGroup = ({ key={`command-result-group-${groupName}-${index}`} dense={true} component={Link} + onClick={() => { + onClick(item); + }} to={item.link} sx={listItemButtonStyle} > diff --git a/frontend/src/component/commandBar/RecentlyVisited/RecentlyVisited.tsx b/frontend/src/component/commandBar/RecentlyVisited/RecentlyVisited.tsx index f18f1632eb..80aa5e3898 100644 --- a/frontend/src/component/commandBar/RecentlyVisited/RecentlyVisited.tsx +++ b/frontend/src/component/commandBar/RecentlyVisited/RecentlyVisited.tsx @@ -16,6 +16,7 @@ import type { LastViewedPage } from 'hooks/useRecentlyVisited'; import type { Theme } from '@mui/material/styles/createTheme'; import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; const listItemButtonStyle = (theme: Theme) => ({ border: `1px solid transparent`, @@ -90,11 +91,23 @@ const RecentlyVisitedFeatureButton = ({ projectId, featureId, }: { key: string; projectId: string; featureId: string }) => { + const { trackEvent } = usePlausibleTracker(); + + const onClick = () => { + trackEvent('command-bar', { + props: { + eventType: `click`, + source: 'recently-visited', + eventTarget: 'Flags', + }, + }); + }; return ( @@ -115,12 +128,25 @@ const RecentlyVisitedPathButton = ({ key, name, }: { path: string; key: string; name: string }) => { + const { trackEvent } = usePlausibleTracker(); + + const onClick = () => { + trackEvent('command-bar', { + props: { + eventType: `click`, + source: 'recently-visited', + eventTarget: 'Pages', + pageType: name, + }, + }); + }; return ( { + const { trackEvent } = usePlausibleTracker(); const { project, loading } = useProjectOverview(projectId); const projectDeleted = !project.name && !loading; + + const onClick = () => { + trackEvent('command-bar', { + props: { + eventType: `click`, + source: 'recently-visited', + eventTarget: 'Projects', + }, + }); + }; + if (projectDeleted) return null; return ( diff --git a/frontend/src/hooks/usePlausibleTracker.ts b/frontend/src/hooks/usePlausibleTracker.ts index af7cc1a315..c7152ab37a 100644 --- a/frontend/src/hooks/usePlausibleTracker.ts +++ b/frontend/src/hooks/usePlausibleTracker.ts @@ -61,7 +61,8 @@ export type CustomEvents = | 'insights-share' | 'many-strategies' | 'sdk-banner' - | 'feature-lifecycle'; + | 'feature-lifecycle' + | 'command-bar'; export const usePlausibleTracker = () => { const plausible = useContext(PlausibleContext);