Compare commits

...

3 Commits

Author SHA1 Message Date
lebaudantoine
c6a45ce58a 📈(frontend) track user interactions from left side panel
Add analytics tracking for actions initiated from the sidebar to monitor
feature usage patterns and user engagement metrics.
2025-05-25 01:07:25 +02:00
lebaudantoine
3cfcdcf036 📈(frontend) add tracking for AI actions usage and success rates
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.
2025-05-25 01:07:19 +02:00
lebaudantoine
69006f0ef0 (frontend) add capture method support to PostHogAnalytics wrapper
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.
2025-05-25 01:00:57 +02:00
4 changed files with 47 additions and 5 deletions

View File

@@ -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<AIMenuItemTransform>) => {
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);
};

View File

@@ -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();
};

View File

@@ -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() {

View File

@@ -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;
}
}