From 346600cc7ad3043f54a0e08cc9cd0f1b5c45c266 Mon Sep 17 00:00:00 2001 From: Pedro Bonamin <46196328+pedrobonamin@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:17:01 +0200 Subject: [PATCH] feat(tasks): add telemetry events to tasks (#6246) * feat(tasks): add telemetry events to tasks * fix(tasks): title case for events and reduce number of events * fix(tasks): remove comment added event from tasks.telemetry --- .../tasks/__telemetry__/tasks.telemetry.ts | 46 +++++++++++++++++++ .../components/form/fields/StatusSelector.tsx | 16 ++++++- .../form/tasksFormBuilder/FormCreate.tsx | 14 ++++-- .../form/tasksFormBuilder/FormEdit.tsx | 10 +++- .../navigation/TasksNavigationProvider.tsx | 9 +++- 5 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 packages/sanity/src/tasks/__telemetry__/tasks.telemetry.ts diff --git a/packages/sanity/src/tasks/__telemetry__/tasks.telemetry.ts b/packages/sanity/src/tasks/__telemetry__/tasks.telemetry.ts new file mode 100644 index 00000000000..1ddaf5d5280 --- /dev/null +++ b/packages/sanity/src/tasks/__telemetry__/tasks.telemetry.ts @@ -0,0 +1,46 @@ +import {defineEvent} from '@sanity/telemetry' + +// Task is created. +export const TaskCreated = defineEvent({ + name: 'Task Created', + version: 1, + description: 'A task is created', +}) + +// Task status changed. %from% - %to% +export const TaskStatusChanged = defineEvent<{ + from: string + to: string +}>({ + name: 'Task Status Changed', + version: 1, + description: 'Task status changed', +}) + +// A task is duplicated +export const TaskDuplicated = defineEvent({ + name: 'Task Duplicated', + version: 1, + description: 'A task is duplicated', +}) + +// A task is removed +export const TaskRemoved = defineEvent({ + name: 'Task Removed', + version: 1, + description: 'A task is removed', +}) + +// The link to a task is copied +export const TaskLinkCopied = defineEvent({ + name: 'Task Link Copied', + version: 1, + description: 'The link to a task is copied', +}) + +// User visited the studio through a link with a task +export const TaskLinkOpened = defineEvent({ + name: 'Task Link Opened', + version: 1, + description: 'User visited the studio through a link with a task', +}) diff --git a/packages/sanity/src/tasks/src/tasks/components/form/fields/StatusSelector.tsx b/packages/sanity/src/tasks/src/tasks/components/form/fields/StatusSelector.tsx index 41ccd1db6cf..ac9e0129411 100644 --- a/packages/sanity/src/tasks/src/tasks/components/form/fields/StatusSelector.tsx +++ b/packages/sanity/src/tasks/src/tasks/components/form/fields/StatusSelector.tsx @@ -1,6 +1,7 @@ import {CheckmarkIcon, CircleIcon} from '@sanity/icons' +import {useTelemetry} from '@sanity/telemetry/react' import {Menu} from '@sanity/ui' -import {type ForwardedRef, forwardRef} from 'react' +import {type ForwardedRef, forwardRef, useCallback} from 'react' import { type FormPatch, isString, @@ -12,6 +13,7 @@ import { } from 'sanity' import {Button, MenuButton, MenuItem} from '../../../../../../ui-components' +import {TaskStatusChanged} from '../../../../../__telemetry__/tasks.telemetry' import {tasksLocaleNamespace} from '../../../../../i18n' import {TASK_STATUS} from '../../../constants/TaskStatus' @@ -45,6 +47,16 @@ interface StatusSelectorProps { export function StatusSelector(props: StatusSelectorProps) { const {value, onChange, options, path} = props + const telemetry = useTelemetry() + + const handleStatusChange = useCallback( + (next?: string) => { + onChange(set(next, path)) + telemetry.log(TaskStatusChanged, {from: value, to: next}) + }, + [onChange, path, telemetry, value], + ) + return ( } @@ -62,7 +74,7 @@ export function StatusSelector(props: StatusSelectorProps) { pressed={isSelected} iconRight={isSelected && } // eslint-disable-next-line react/jsx-no-bind - onClick={() => onChange(set(option.value, path))} + onClick={() => handleStatusChange(option.value)} /> ) })} diff --git a/packages/sanity/src/tasks/src/tasks/components/form/tasksFormBuilder/FormCreate.tsx b/packages/sanity/src/tasks/src/tasks/components/form/tasksFormBuilder/FormCreate.tsx index 2197582c540..fb2d69409b9 100644 --- a/packages/sanity/src/tasks/src/tasks/components/form/tasksFormBuilder/FormCreate.tsx +++ b/packages/sanity/src/tasks/src/tasks/components/form/tasksFormBuilder/FormCreate.tsx @@ -1,9 +1,11 @@ import {TrashIcon} from '@sanity/icons' +import {useTelemetry} from '@sanity/telemetry/react' import {Box, Flex, Switch, Text, useToast} from '@sanity/ui' import {useCallback, useState} from 'react' import {type ObjectInputProps, set, useTranslation} from 'sanity' import {Button} from '../../../../../../ui-components' +import {TaskCreated} from '../../../../../__telemetry__/tasks.telemetry' import {tasksLocaleNamespace} from '../../../../../i18n' import {useTasksNavigation} from '../../../context' import {useRemoveTask} from '../../../hooks/useRemoveTask' @@ -26,16 +28,18 @@ const getTaskSubscribers = (task: TaskDocument): string[] => { return subscribers } export function FormCreate(props: ObjectInputProps) { - const [createMore, setCreateMore] = useState(false) + const {onChange} = props const { setViewMode, setActiveTab, state: {viewMode}, } = useTasksNavigation() - const toast = useToast() + const telemetry = useTelemetry() + + const [createMore, setCreateMore] = useState(false) const handleCreateMore = useCallback(() => setCreateMore((p) => !p), []) - const {onChange} = props + const value = props.value as TaskDocument const onRemove = useCallback(() => { setViewMode({type: 'list'}) @@ -62,12 +66,14 @@ export function FormCreate(props: ObjectInputProps) { setActiveTab('subscribed') } + telemetry.log(TaskCreated) + toast.push({ closable: true, status: 'success', title: t('form.status.success'), }) - }, [setViewMode, setActiveTab, onChange, createMore, toast, value, t]) + }, [value, onChange, createMore, telemetry, toast, t, setViewMode, setActiveTab]) return ( <> diff --git a/packages/sanity/src/tasks/src/tasks/components/form/tasksFormBuilder/FormEdit.tsx b/packages/sanity/src/tasks/src/tasks/components/form/tasksFormBuilder/FormEdit.tsx index 2aa20b4c3ac..366e303be76 100644 --- a/packages/sanity/src/tasks/src/tasks/components/form/tasksFormBuilder/FormEdit.tsx +++ b/packages/sanity/src/tasks/src/tasks/components/form/tasksFormBuilder/FormEdit.tsx @@ -1,4 +1,5 @@ import {CopyIcon, LinkIcon, TrashIcon} from '@sanity/icons' +import {useTelemetry} from '@sanity/telemetry/react' import {Box, Card, Flex, Menu, MenuDivider, Stack} from '@sanity/ui' // eslint-disable-next-line camelcase import {getTheme_v2} from '@sanity/ui/theme' @@ -19,6 +20,7 @@ import {css, styled} from 'styled-components' import {CommentsProvider} from '../../../../../../structure/comments' import {MenuButton, MenuItem, TooltipDelayGroupProvider} from '../../../../../../ui-components' +import {TaskDuplicated, TaskRemoved} from '../../../../../__telemetry__/tasks.telemetry' import {tasksLocaleNamespace} from '../../../../../i18n' import {useTasksEnabled, useTasksNavigation} from '../../../context' import {useActivityLog} from '../../../hooks/useActivityLog' @@ -44,14 +46,18 @@ const FirstRow = styled(Flex)((props) => { function FormActionsMenu({id, value}: {id: string; value: TaskDocument}) { const {setViewMode, handleCopyLinkToTask} = useTasksNavigation() const {mode} = useTasksEnabled() + const telemetry = useTelemetry() + const onTaskRemoved = useCallback(() => { setViewMode({type: 'list'}) - }, [setViewMode]) + telemetry.log(TaskRemoved) + }, [setViewMode, telemetry]) const removeTask = useRemoveTask({id, onRemoved: onTaskRemoved}) const duplicateTask = useCallback(() => { setViewMode({type: 'duplicate', duplicateTaskValues: value}) - }, [setViewMode, value]) + telemetry.log(TaskDuplicated) + }, [setViewMode, telemetry, value]) const {t} = useTranslation(tasksLocaleNamespace) diff --git a/packages/sanity/src/tasks/src/tasks/context/navigation/TasksNavigationProvider.tsx b/packages/sanity/src/tasks/src/tasks/context/navigation/TasksNavigationProvider.tsx index 3dde28c2cf8..1a43f76b29c 100644 --- a/packages/sanity/src/tasks/src/tasks/context/navigation/TasksNavigationProvider.tsx +++ b/packages/sanity/src/tasks/src/tasks/context/navigation/TasksNavigationProvider.tsx @@ -1,8 +1,10 @@ +import {useTelemetry} from '@sanity/telemetry/react' import {useToast} from '@sanity/ui' import {uuid} from '@sanity/uuid' import {type ReactNode, useCallback, useEffect, useReducer} from 'react' import {useRouter} from 'sanity/router' +import {TaskLinkCopied, TaskLinkOpened} from '../../../../__telemetry__/tasks.telemetry' import {TasksNavigationContext} from './TasksNavigationContext' import {type Action, type SidebarTabsIds, type State, type ViewModeOptions} from './types' @@ -73,6 +75,7 @@ export const TasksNavigationProvider = ({children}: {children: ReactNode}) => { const [state, dispatch] = useReducer(reducer, initialState) const router = useRouter() const toast = useToast() + const telemetry = useTelemetry() const setViewMode = useCallback((viewMode: ViewModeOptions) => { switch (viewMode.type) { @@ -126,6 +129,7 @@ export const TasksNavigationProvider = ({children}: {children: ReactNode}) => { status: 'info', title: 'Copied link to clipboard', }) + telemetry.log(TaskLinkCopied) }) .catch(() => { toast.push({ @@ -134,7 +138,7 @@ export const TasksNavigationProvider = ({children}: {children: ReactNode}) => { title: 'Failed to copy link to clipboard', }) }) - }, [state.selectedTask, state.viewMode, toast]) + }, [state.selectedTask, state.viewMode, telemetry, toast]) // This is casted to a string to make it stable across renders so it doesn't trigger multiple times the effect. const searchParamsAsString = new URLSearchParams(router.state._searchParams).toString() @@ -152,9 +156,10 @@ export const TasksNavigationProvider = ({children}: {children: ReactNode}) => { const selectedTask = searchParams.get('selectedTask') if (viewMode === 'edit' && selectedTask) { dispatch({type: 'EDIT_TASK', payload: {id: selectedTask}}) + telemetry.log(TaskLinkOpened) } } - }, [searchParamsAsString]) + }, [searchParamsAsString, telemetry]) return (