diff --git a/frontend/src/AppRoutes/index.tsx b/frontend/src/AppRoutes/index.tsx index 97b77917fa..4c46928163 100644 --- a/frontend/src/AppRoutes/index.tsx +++ b/frontend/src/AppRoutes/index.tsx @@ -76,9 +76,9 @@ function App(): JSX.Element { useEffect(() => { if (isLoggedInState && user && user.userId && user.email) { - window.analytics.identify(user?.userId, { - email: user?.email || '', - name: user?.name || '', + window.analytics.identify(user?.email, { + email: user?.email, + name: user?.name, }); } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/container/OnboardingContainer/APM/APM.tsx b/frontend/src/container/OnboardingContainer/APM/APM.tsx index 8939af51e9..c2bee3ed69 100644 --- a/frontend/src/container/OnboardingContainer/APM/APM.tsx +++ b/frontend/src/container/OnboardingContainer/APM/APM.tsx @@ -3,7 +3,8 @@ import './APM.styles.scss'; import cx from 'classnames'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; +import { trackEvent } from 'utils/segmentAnalytics'; import GoLang from './GoLang/GoLang'; import Java from './Java/Java'; @@ -36,6 +37,15 @@ export default function APM({ }): JSX.Element { const [selectedLanguage, setSelectedLanguage] = useState('java'); + useEffect(() => { + // on language select + trackEvent('Onboarding: APM', { + selectedLanguage, + activeStep, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedLanguage]); + const renderSelectedLanguageSetupInstructions = (): JSX.Element => { switch (selectedLanguage) { case 'java': diff --git a/frontend/src/container/OnboardingContainer/APM/Java/Java.tsx b/frontend/src/container/OnboardingContainer/APM/Java/Java.tsx index a5338ba967..97fd2f907a 100644 --- a/frontend/src/container/OnboardingContainer/APM/Java/Java.tsx +++ b/frontend/src/container/OnboardingContainer/APM/Java/Java.tsx @@ -3,7 +3,8 @@ import './Java.styles.scss'; import { MDXProvider } from '@mdx-js/react'; import { Form, Input, Select } from 'antd'; import Header from 'container/OnboardingContainer/common/Header/Header'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; +import { trackEvent } from 'utils/segmentAnalytics'; import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus'; import JavaDocs from './md-docs/java.md'; @@ -27,6 +28,14 @@ export default function Java({ const [form] = Form.useForm(); + useEffect(() => { + // on language select + trackEvent('Onboarding: APM : Java', { + selectedFrameWork, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedFrameWork]); + const renderDocs = (): JSX.Element => { switch (selectedFrameWork) { case 'tomcat': diff --git a/frontend/src/container/OnboardingContainer/APM/Javascript/Javascript.tsx b/frontend/src/container/OnboardingContainer/APM/Javascript/Javascript.tsx index 9c2e38c143..bff9406f13 100644 --- a/frontend/src/container/OnboardingContainer/APM/Javascript/Javascript.tsx +++ b/frontend/src/container/OnboardingContainer/APM/Javascript/Javascript.tsx @@ -3,7 +3,8 @@ import './Javascript.styles.scss'; import { MDXProvider } from '@mdx-js/react'; import { Form, Input, Select } from 'antd'; import Header from 'container/OnboardingContainer/common/Header/Header'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; +import { trackEvent } from 'utils/segmentAnalytics'; import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus'; import ExpressDocs from './md-docs/express.md'; @@ -25,6 +26,14 @@ export default function Javascript({ const [form] = Form.useForm(); + useEffect(() => { + // on language select + trackEvent('Onboarding: APM : Javascript', { + selectedFrameWork, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedFrameWork]); + const renderDocs = (): JSX.Element => { switch (selectedFrameWork) { case 'nodejs': diff --git a/frontend/src/container/OnboardingContainer/APM/Python/Python.tsx b/frontend/src/container/OnboardingContainer/APM/Python/Python.tsx index 51331fbd70..31d28db700 100644 --- a/frontend/src/container/OnboardingContainer/APM/Python/Python.tsx +++ b/frontend/src/container/OnboardingContainer/APM/Python/Python.tsx @@ -3,7 +3,8 @@ import './Python.styles.scss'; import { MDXProvider } from '@mdx-js/react'; import { Form, Input, Select } from 'antd'; import Header from 'container/OnboardingContainer/common/Header/Header'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; +import { trackEvent } from 'utils/segmentAnalytics'; import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus'; import DjangoDocs from './md-docs/django.md'; @@ -29,6 +30,14 @@ export default function Python({ const [form] = Form.useForm(); + useEffect(() => { + // on language select + trackEvent('Onboarding: APM : Python', { + selectedFrameWork, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedFrameWork]); + const renderDocs = (): JSX.Element => { switch (selectedFrameWork) { case 'django': diff --git a/frontend/src/container/OnboardingContainer/APM/common/ConnectionStatus/ConnectionStatus.tsx b/frontend/src/container/OnboardingContainer/APM/common/ConnectionStatus/ConnectionStatus.tsx index 7204b5819a..de918dced1 100644 --- a/frontend/src/container/OnboardingContainer/APM/common/ConnectionStatus/ConnectionStatus.tsx +++ b/frontend/src/container/OnboardingContainer/APM/common/ConnectionStatus/ConnectionStatus.tsx @@ -16,6 +16,7 @@ import { UPDATE_TIME_INTERVAL } from 'types/actions/globalTime'; import { PayloadProps as QueryServicePayloadProps } from 'types/api/metrics/getService'; import { GlobalReducer } from 'types/reducer/globalTime'; import { Tags } from 'types/reducer/trace'; +import { trackEvent } from 'utils/segmentAnalytics'; interface ConnectionStatusProps { serviceName: string; @@ -112,6 +113,10 @@ export default function ConnectionStatus({ if (data || isError) { setRetryCount(retryCount - 1); if (retryCount < 0) { + trackEvent('❌ Onboarding: APM: Connection Status', { + serviceName, + status: 'Failed', + }); setLoading(false); } } @@ -122,6 +127,11 @@ export default function ConnectionStatus({ setLoading(false); setIsReceivingData(true); + trackEvent('✅ Onboarding: APM: Connection Status', { + serviceName, + status: 'Successful', + }); + break; } } @@ -130,31 +140,35 @@ export default function ConnectionStatus({ // Use useEffect to update query parameters when the polling interval lapses useEffect(() => { - const pollingTimer = setInterval(() => { - // Trigger a refetch with the updated parameters - const updatedMinTime = (Date.now() - 15 * 60 * 1000) * 1000000; - const updatedMaxTime = Date.now() * 1000000; + let pollingTimer: string | number | NodeJS.Timer | undefined; - const payload = { - maxTime: updatedMaxTime, - minTime: updatedMinTime, - selectedTime, - }; + if (loading) { + pollingTimer = setInterval(() => { + // Trigger a refetch with the updated parameters + const updatedMinTime = (Date.now() - 15 * 60 * 1000) * 1000000; + const updatedMaxTime = Date.now() * 1000000; - dispatch({ - type: UPDATE_TIME_INTERVAL, - payload, - }); + const payload = { + maxTime: updatedMaxTime, + minTime: updatedMinTime, + selectedTime, + }; - // refetch(updatedParams); - }, pollingInterval); // Same interval as pollingInterval + dispatch({ + type: UPDATE_TIME_INTERVAL, + payload, + }); + }, pollingInterval); // Same interval as pollingInterval + } else if (!loading && pollingTimer) { + clearInterval(pollingTimer); + } // Clean up the interval when the component unmounts return (): void => { clearInterval(pollingTimer); }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [refetch, selectedTags, selectedTime]); + }, [refetch, selectedTags, selectedTime, loading]); useEffect(() => { verifyApplicationData(data); diff --git a/frontend/src/container/OnboardingContainer/LogsManagement/ExistingCollectors/ExistingCollectors.tsx b/frontend/src/container/OnboardingContainer/LogsManagement/ExistingCollectors/ExistingCollectors.tsx index b7d95238cf..a0ccead4a8 100644 --- a/frontend/src/container/OnboardingContainer/LogsManagement/ExistingCollectors/ExistingCollectors.tsx +++ b/frontend/src/container/OnboardingContainer/LogsManagement/ExistingCollectors/ExistingCollectors.tsx @@ -1,7 +1,8 @@ import { MDXProvider } from '@mdx-js/react'; import { Select } from 'antd'; import Header from 'container/OnboardingContainer/common/Header/Header'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; +import { trackEvent } from 'utils/segmentAnalytics'; import FluentBit from './md-docs/fluentBit.md'; import FluentD from './md-docs/fluentD.md'; @@ -16,6 +17,14 @@ enum FrameworksMap { export default function ExistingCollectors(): JSX.Element { const [selectedFrameWork, setSelectedFrameWork] = useState('fluent_d'); + useEffect(() => { + // on language select + trackEvent('Onboarding: Logs Management: Existing Collectors', { + selectedFrameWork, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedFrameWork]); + const renderDocs = (): JSX.Element => { switch (selectedFrameWork) { case 'fluent_d': diff --git a/frontend/src/container/OnboardingContainer/LogsManagement/LogsManagement.tsx b/frontend/src/container/OnboardingContainer/LogsManagement/LogsManagement.tsx index e880452ac5..de25134bb4 100644 --- a/frontend/src/container/OnboardingContainer/LogsManagement/LogsManagement.tsx +++ b/frontend/src/container/OnboardingContainer/LogsManagement/LogsManagement.tsx @@ -4,7 +4,8 @@ import './LogsManagement.styles.scss'; import cx from 'classnames'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; +import { trackEvent } from 'utils/segmentAnalytics'; import ApplicationLogs from './ApplicationLogs/ApplicationLogs'; import Docker from './Docker/Docker'; @@ -60,6 +61,15 @@ export default function LogsManagement({ }): JSX.Element { const [selectedLogsType, setSelectedLogsType] = useState('kubernetes'); + useEffect(() => { + // on language select + trackEvent('Onboarding: Logs Management', { + selectedLogsType, + activeStep, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedLogsType]); + const renderSelectedLanguageSetupInstructions = (): | JSX.Element | undefined => { diff --git a/frontend/src/container/OnboardingContainer/LogsManagement/common/LogsConnectionStatus/LogsConnectionStatus.tsx b/frontend/src/container/OnboardingContainer/LogsManagement/common/LogsConnectionStatus/LogsConnectionStatus.tsx index 9fd9a2958e..0ceee7b967 100644 --- a/frontend/src/container/OnboardingContainer/LogsManagement/common/LogsConnectionStatus/LogsConnectionStatus.tsx +++ b/frontend/src/container/OnboardingContainer/LogsManagement/common/LogsConnectionStatus/LogsConnectionStatus.tsx @@ -16,6 +16,7 @@ import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { EQueryType } from 'types/common/dashboard'; import { DataSource } from 'types/common/queryBuilder'; +import { trackEvent } from 'utils/segmentAnalytics'; interface ConnectionStatusProps { logType: string; @@ -95,6 +96,10 @@ export default function LogsConnectionStatus({ setRetryCount(retryCount - 1); if (retryCount < 0) { + trackEvent('❌ Onboarding: Logs Management: Connection Status', { + status: 'Failed', + }); + setLoading(false); setPollingInterval(false); } @@ -123,6 +128,11 @@ export default function LogsConnectionStatus({ setIsReceivingData(true); setRetryCount(-1); setPollingInterval(false); + + trackEvent('✅ Onboarding: Logs Management: Connection Status', { + status: 'Successful', + }); + break; } } diff --git a/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx b/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx index edee0cb70f..72d93cc4fa 100644 --- a/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx +++ b/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx @@ -9,6 +9,8 @@ import ROUTES from 'constants/routes'; import { useIsDarkMode } from 'hooks/useDarkMode'; import history from 'lib/history'; import { useEffect, useState } from 'react'; +import { useEffectOnce } from 'react-use'; +import { trackEvent } from 'utils/segmentAnalytics'; import APM from './APM/APM'; import InfrastructureMonitoring from './InfrastructureMonitoring/InfrastructureMonitoring'; @@ -98,6 +100,10 @@ export default function Onboarding(): JSX.Element { }, ]; + useEffectOnce(() => { + trackEvent('Onboarding Started'); + }); + useEffect(() => { if (selectedModule?.id === ModulesMap.InfrastructureMonitoring) { setsteps([...baseSteps]); @@ -123,27 +129,55 @@ export default function Onboarding(): JSX.Element { }, ]); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedModule, selectedLogsType]); + useEffect(() => { + // on select + trackEvent('Onboarding: Module Selected', { + selectedModule: selectedModule.id, + }); + }, [selectedModule]); + const handleNext = (): void => { // Need to add logic to validate service name and then allow next step transition in APM module const isFormValid = true; if (isFormValid && activeStep <= 3) { - setActiveStep(activeStep + 1); + const nextStep = activeStep + 1; + + // on next + trackEvent('Onboarding: Next', { + selectedModule: selectedModule.id, + nextStepId: nextStep, + }); + + setActiveStep(nextStep); setCurrent(current + 1); } }; const handlePrev = (): void => { if (activeStep >= 1) { + const prevStep = activeStep - 1; + + // on prev + trackEvent('Onboarding: Back', { + module: selectedModule.id, + prevStepId: prevStep, + }); + setCurrent(current - 1); - setActiveStep(activeStep - 1); + setActiveStep(prevStep); } }; const handleOnboardingComplete = (): void => { + trackEvent('Onboarding Complete', { + module: selectedModule.id, + }); + switch (selectedModule.id) { case ModulesMap.APM: history.push(ROUTES.APPLICATION); @@ -160,8 +194,15 @@ export default function Onboarding(): JSX.Element { }; const handleStepChange = (value: number): void => { + const stepId = value + 1; + + trackEvent('Onboarding: Step Change', { + module: selectedModule.id, + step: stepId, + }); + setCurrent(value); - setActiveStep(value + 1); + setActiveStep(stepId); }; const handleModuleSelect = (module: ModuleProps): void => { diff --git a/frontend/src/utils/segmentAnalytics.ts b/frontend/src/utils/segmentAnalytics.ts index d776c34c3c..d01ba398e9 100644 --- a/frontend/src/utils/segmentAnalytics.ts +++ b/frontend/src/utils/segmentAnalytics.ts @@ -4,7 +4,7 @@ function trackPageView(pageName: string): void { function trackEvent( eventName: string, - properties: Record, + properties?: Record, ): void { window.analytics.track(eventName, properties); }