From 69006f0ef0b11eb4d6b5c66915e70e581ee6bc80 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Sun, 25 May 2025 00:30:32 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8(frontend)=20add=20capture=20metho?= =?UTF-8?q?d=20support=20to=20PostHogAnalytics=20wrapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement capture method in PostHogAnalytics wrapper to support direct event tracking through PostHog SDK. Enables more granular analytics event handling within the application's analytics abstraction layer. --- src/frontend/apps/impress/src/libs/Analytics.tsx | 10 +++++++++- .../apps/impress/src/services/PosthogAnalytic.tsx | 12 +++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/frontend/apps/impress/src/libs/Analytics.tsx b/src/frontend/apps/impress/src/libs/Analytics.tsx index d1b775b5ff..28cebfe0fa 100644 --- a/src/frontend/apps/impress/src/libs/Analytics.tsx +++ b/src/frontend/apps/impress/src/libs/Analytics.tsx @@ -9,7 +9,15 @@ type AnalyticEventUser = { email: string; }; -export type AnalyticEvent = AnalyticEventClick | AnalyticEventUser; +export type AnalyticEventGeneric = { + eventName: string; + [key: string]: string | number; +}; + +export type AnalyticEvent = + | AnalyticEventClick + | AnalyticEventUser + | AnalyticEventGeneric; export abstract class AbstractAnalytic { public constructor() { diff --git a/src/frontend/apps/impress/src/services/PosthogAnalytic.tsx b/src/frontend/apps/impress/src/services/PosthogAnalytic.tsx index 53556c44d7..6bcac66ca3 100644 --- a/src/frontend/apps/impress/src/services/PosthogAnalytic.tsx +++ b/src/frontend/apps/impress/src/services/PosthogAnalytic.tsx @@ -3,7 +3,7 @@ import posthog from 'posthog-js'; import { PostHogProvider as PHProvider } from 'posthog-js/react'; import { JSX, PropsWithChildren, ReactNode, useEffect } from 'react'; -import { AbstractAnalytic, AnalyticEvent } from '@/libs/'; +import { AbstractAnalytic, AnalyticEvent, AnalyticEventGeneric } from '@/libs/'; export class PostHogAnalytic extends AbstractAnalytic { private conf?: PostHogConf = undefined; @@ -19,8 +19,14 @@ export class PostHogAnalytic extends AbstractAnalytic { } public trackEvent(evt: AnalyticEvent): void { - if (evt.eventName === 'user') { - posthog.identify(evt.id, { email: evt.email }); + switch (evt.eventName) { + case 'user': + posthog.identify(evt.id as string, { email: evt.email }); + break; + default: + const { eventName, ...properties } = evt as AnalyticEventGeneric; + posthog.capture(eventName, properties); + break; } } From 3cfcdcf036085b43bb456e769e84994d5acd7781 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Sun, 25 May 2025 01:01:14 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=88(frontend)=20add=20tracking=20f?= =?UTF-8?q?or=20AI=20actions=20usage=20and=20success=20rates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement analytics tracking on AI actions to collect insights on usage patterns and monitor error/success rates. Enables data-driven improvements. Temporary solution until backend implements more precise tracking for comprehensive AI feature analytics. --- .../components/BlockNoteToolBar/AIButton.tsx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/AIButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/AIButton.tsx index 45bd1ed49a..0ce42673d4 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/AIButton.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/AIButton.tsx @@ -16,6 +16,7 @@ import { useTranslation } from 'react-i18next'; import { isAPIError } from '@/api'; import { Box, Icon } from '@/components'; import { useDocOptions, useDocStore } from '@/docs/doc-management/'; +import { useAnalytics } from '@/libs'; import { AITransformActions, @@ -217,22 +218,44 @@ const AIMenuItemTransform = ({ children, icon, }: PropsWithChildren) => { + const { trackEvent } = useAnalytics(); const { mutateAsync: requestAI, isPending } = useDocAITransform(); const editor = useBlockNoteEditor(); const requestAIAction = async (selectedBlocks: Block[]) => { const text = await editor.blocksToMarkdownLossy(selectedBlocks); + const requestStartTime = performance.now(); const responseAI = await requestAI({ text, action, docId, }); + const requestDuration = performance.now() - requestStartTime; + + const eventProperties = { + eventName: 'requestAIAction', + action: action, + docId: docId, + requestLength: text.length, + numberBlocks: selectedBlocks.length, + requestDuration: requestDuration, + }; if (!responseAI?.answer) { + trackEvent({ + ...eventProperties, + status: 'error', + }); throw new Error('No response from AI'); } + trackEvent({ + ...eventProperties, + status: 'success', + responseLength: String(responseAI.answer), + }); + const markdown = await editor.tryParseMarkdownToBlocks(responseAI.answer); editor.replaceBlocks(selectedBlocks, markdown); }; From c6a45ce58a465555da859de11044bb3fba64693c Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Sun, 25 May 2025 01:04:59 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=93=88(frontend)=20track=20user=20int?= =?UTF-8?q?eractions=20from=20left=20side=20panel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add analytics tracking for actions initiated from the sidebar to monitor feature usage patterns and user engagement metrics. --- .../src/features/left-panel/components/LeftPanelHeader.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx index 7177bc2c74..72dc33907f 100644 --- a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx +++ b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx @@ -8,12 +8,14 @@ import { useCreateDoc } from '@/docs/doc-management'; import { DocSearchModal } from '@/docs/doc-search'; import { useAuth } from '@/features/auth'; import { useCmdK } from '@/hook/useCmdK'; +import { useAnalytics } from '@/libs'; import { useLeftPanelStore } from '../stores'; export const LeftPanelHeader = ({ children }: PropsWithChildren) => { const router = useRouter(); const { authenticated } = useAuth(); + const { trackEvent } = useAnalytics(); const [isSearchModalOpen, setIsSearchModalOpen] = useState(false); const openSearchModal = useCallback(() => { @@ -23,8 +25,10 @@ export const LeftPanelHeader = ({ children }: PropsWithChildren) => { return; } + trackEvent({ eventName: 'openSearchModal', position: 'LeftPanelHeader' }); + setIsSearchModalOpen(true); - }, []); + }, [trackEvent]); const closeSearchModal = useCallback(() => { setIsSearchModalOpen(false); @@ -46,6 +50,7 @@ export const LeftPanelHeader = ({ children }: PropsWithChildren) => { }; const createNewDoc = () => { + trackEvent({ eventName: 'createNewDoc', position: 'LeftPanelHeader' }); createDoc(); };