From 73ff89a80a8bd57f3caebcc58fd9ce50fbe2e79c Mon Sep 17 00:00:00 2001 From: manika-signoz Date: Tue, 23 Sep 2025 20:47:39 +0530 Subject: [PATCH] feat: revamp onboarding (#9068) * feat: revamp onboarding, send list to mixpanel, join logic to convert to single string * chore: props changes * fix: allow user to proceed even if api fails * chore: remove console.log * chore: remove commented code * chore: minor colour tweaks * chore: resolve comments --- .../AboutSigNozQuestions.tsx | 100 ++++++----- .../OnboardingQuestionaire.styles.scss | 51 ++++++ .../OrgQuestions/OrgQuestions.tsx | 166 ++++++------------ .../OnboardingQuestionaire/index.tsx | 19 +- frontend/src/types/api/onboarding/types.ts | 2 +- 5 files changed, 177 insertions(+), 161 deletions(-) diff --git a/frontend/src/container/OnboardingQuestionaire/AboutSigNozQuestions/AboutSigNozQuestions.tsx b/frontend/src/container/OnboardingQuestionaire/AboutSigNozQuestions/AboutSigNozQuestions.tsx index 2124e995150f..913da1ec351d 100644 --- a/frontend/src/container/OnboardingQuestionaire/AboutSigNozQuestions/AboutSigNozQuestions.tsx +++ b/frontend/src/container/OnboardingQuestionaire/AboutSigNozQuestions/AboutSigNozQuestions.tsx @@ -2,14 +2,14 @@ import '../OnboardingQuestionaire.styles.scss'; import { Color } from '@signozhq/design-tokens'; -import { Button, Input, Typography } from 'antd'; +import { Button, Checkbox, Input, Typography } from 'antd'; import TextArea from 'antd/lib/input/TextArea'; import logEvent from 'api/common/logEvent'; import { ArrowLeft, ArrowRight, CheckCircle } from 'lucide-react'; import { useEffect, useState } from 'react'; export interface SignozDetails { - interestInSignoz: string | null; + interestInSignoz: string[] | null; otherInterestInSignoz: string | null; discoverSignoz: string | null; } @@ -22,9 +22,12 @@ interface AboutSigNozQuestionsProps { } const interestedInOptions: Record = { - savingCosts: 'Saving costs', - otelNativeStack: 'Interested in Otel-native stack', - allInOne: 'All in one (Logs, Metrics & Traces)', + loweringCosts: 'Lowering observability costs', + otelNativeStack: 'Interested in OTel-native stack', + deploymentFlexibility: 'Deployment flexibility (Cloud/Self-Host) in future', + singleTool: + 'Single Tool (logs, metrics & traces) to reduce operational overhead', + correlateSignals: 'Correlate signals for faster troubleshooting', }; export function AboutSigNozQuestions({ @@ -33,8 +36,8 @@ export function AboutSigNozQuestions({ onNext, onBack, }: AboutSigNozQuestionsProps): JSX.Element { - const [interestInSignoz, setInterestInSignoz] = useState( - signozDetails?.interestInSignoz || null, + const [interestInSignoz, setInterestInSignoz] = useState( + signozDetails?.interestInSignoz || [], ); const [otherInterestInSignoz, setOtherInterestInSignoz] = useState( signozDetails?.otherInterestInSignoz || '', @@ -47,8 +50,8 @@ export function AboutSigNozQuestions({ useEffect((): void => { if ( discoverSignoz !== '' && - interestInSignoz !== null && - (interestInSignoz !== 'Others' || otherInterestInSignoz !== '') + interestInSignoz.length > 0 && + (!interestInSignoz.includes('Others') || otherInterestInSignoz !== '') ) { setIsNextDisabled(false); } else { @@ -56,6 +59,14 @@ export function AboutSigNozQuestions({ } }, [interestInSignoz, otherInterestInSignoz, discoverSignoz]); + const handleInterestChange = (option: string, checked: boolean): void => { + if (checked) { + setInterestInSignoz((prev) => [...prev, option]); + } else { + setInterestInSignoz((prev) => prev.filter((item) => item !== option)); + } + }; + const handleOnNext = (): void => { setSignozDetails({ discoverSignoz, @@ -108,50 +119,45 @@ export function AboutSigNozQuestions({
What got you interested in SigNoz?
-
+
{Object.keys(interestedInOptions).map((option: string) => ( - +
+ handleInterestChange(option, e.target.checked)} + > + {interestedInOptions[option]} + +
))} - {interestInSignoz === 'Others' ? ( - - ) : ( - '' - ) +
+ + handleInterestChange('Others', e.target.checked) } - onChange={(e): void => setOtherInterestInSignoz(e.target.value)} - /> - ) : ( - - )} + + {interestInSignoz.includes('Others') && ( + + ) : ( + '' + ) + } + onChange={(e): void => setOtherInterestInSignoz(e.target.value)} + /> + )} +
diff --git a/frontend/src/container/OnboardingQuestionaire/OnboardingQuestionaire.styles.scss b/frontend/src/container/OnboardingQuestionaire/OnboardingQuestionaire.styles.scss index 70f68070f862..e266e450dbca 100644 --- a/frontend/src/container/OnboardingQuestionaire/OnboardingQuestionaire.styles.scss +++ b/frontend/src/container/OnboardingQuestionaire/OnboardingQuestionaire.styles.scss @@ -94,6 +94,7 @@ border-radius: 4px; font-size: 14px; padding: 12px; + font-weight: 400; &::placeholder { color: var(--bg-vanilla-400); @@ -290,6 +291,37 @@ gap: 10px; } + .checkbox-grid { + display: flex; + flex-direction: column; + gap: 12px; + margin-top: 12px; + } + + .checkbox-item { + display: flex; + flex-direction: column; + gap: 8px; + + .ant-checkbox-wrapper { + color: var(--bg-vanilla-400); + font-size: 14px; + font-weight: 400; + + .ant-checkbox { + .ant-checkbox-inner { + border-color: var(--bg-slate-100); + background-color: var(--bg-ink-200); + } + + &.ant-checkbox-checked .ant-checkbox-inner { + background-color: var(--bg-robin-500); + border-color: var(--bg-robin-500); + } + } + } + } + .onboarding-questionaire-button, .add-another-member-button, .remove-team-member-button { @@ -466,6 +498,7 @@ border: 1px solid var(--bg-vanilla-300); background: var(--bg-vanilla-100); color: var(--text-ink-300); + font-weight: 400; &::placeholder { color: var(--bg-slate-400); @@ -527,6 +560,24 @@ color: var(--bg-slate-300); } + .checkbox-item { + .ant-checkbox-wrapper { + color: var(--bg-ink-300); + + .ant-checkbox { + .ant-checkbox-inner { + border-color: var(--bg-vanilla-300); + background-color: var(--bg-vanilla-100); + } + + &.ant-checkbox-checked .ant-checkbox-inner { + background-color: var(--bg-robin-500); + border-color: var(--bg-robin-500); + } + } + } + } + input[type='text'] { border: 1px solid var(--bg-vanilla-300); background: var(--bg-vanilla-100); diff --git a/frontend/src/container/OnboardingQuestionaire/OrgQuestions/OrgQuestions.tsx b/frontend/src/container/OnboardingQuestionaire/OrgQuestions/OrgQuestions.tsx index 71c8aa2f2e82..6cd6e224d156 100644 --- a/frontend/src/container/OnboardingQuestionaire/OrgQuestions/OrgQuestions.tsx +++ b/frontend/src/container/OnboardingQuestionaire/OrgQuestions/OrgQuestions.tsx @@ -38,6 +38,7 @@ const observabilityTools = { AzureAppMonitor: 'Azure App Monitor', GCPNativeO11yTools: 'GCP-native o11y tools', Honeycomb: 'Honeycomb', + None: 'None/Starting fresh', }; function OrgQuestions({ @@ -53,9 +54,6 @@ function OrgQuestions({ const [organisationName, setOrganisationName] = useState( orgDetails?.organisationName || '', ); - const [usesObservability, setUsesObservability] = useState( - orgDetails?.usesObservability || null, - ); const [observabilityTool, setObservabilityTool] = useState( orgDetails?.observabilityTool || null, ); @@ -83,7 +81,7 @@ function OrgQuestions({ orgDetails.organisationName === organisationName ) { logEvent('Org Onboarding: Answered', { - usesObservability, + usesObservability: !observabilityTool?.includes('None'), observabilityTool, otherTool, usesOtel, @@ -91,7 +89,7 @@ function OrgQuestions({ onNext({ organisationName, - usesObservability, + usesObservability: !observabilityTool?.includes('None'), observabilityTool, otherTool, usesOtel, @@ -114,7 +112,7 @@ function OrgQuestions({ }); logEvent('Org Onboarding: Answered', { - usesObservability, + usesObservability: !observabilityTool?.includes('None'), observabilityTool, otherTool, usesOtel, @@ -122,7 +120,7 @@ function OrgQuestions({ onNext({ organisationName, - usesObservability, + usesObservability: !observabilityTool?.includes('None'), observabilityTool, otherTool, usesOtel, @@ -152,16 +150,16 @@ function OrgQuestions({ }; const isValidUsesObservability = (): boolean => { - if (usesObservability === null) { - return false; - } - - if (usesObservability && (!observabilityTool || observabilityTool === '')) { + if (!observabilityTool || observabilityTool === '') { return false; } // eslint-disable-next-line sonarjs/prefer-single-boolean-return - if (usesObservability && observabilityTool === 'Others' && otherTool === '') { + if ( + !observabilityTool?.includes('None') && + observabilityTool === 'Others' && + otherTool === '' + ) { return false; } @@ -177,13 +175,7 @@ function OrgQuestions({ setIsNextDisabled(true); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - organisationName, - usesObservability, - usesOtel, - observabilityTool, - otherTool, - ]); + }, [organisationName, usesOtel, observabilityTool, otherTool]); const handleOnNext = (): void => { handleOrgNameUpdate(); @@ -217,99 +209,57 @@ function OrgQuestions({
-
- {usesObservability && ( -
- -
- {Object.keys(observabilityTools).map((tool) => ( - - ))} - - {observabilityTool === 'Others' ? ( - - ) : ( - '' - ) - } - onChange={(e): void => setOtherTool(e.target.value)} - /> - ) : ( - - )} -
-
- )} -
Do you already use OpenTelemetry?
diff --git a/frontend/src/container/OnboardingQuestionaire/index.tsx b/frontend/src/container/OnboardingQuestionaire/index.tsx index 57832e28c2c5..435902511f8d 100644 --- a/frontend/src/container/OnboardingQuestionaire/index.tsx +++ b/frontend/src/container/OnboardingQuestionaire/index.tsx @@ -46,7 +46,7 @@ const INITIAL_ORG_DETAILS: OrgDetails = { }; const INITIAL_SIGNOZ_DETAILS: SignozDetails = { - interestInSignoz: '', + interestInSignoz: [], otherInterestInSignoz: '', discoverSignoz: '', }; @@ -145,6 +145,9 @@ function OnboardingQuestionaire(): JSX.Element { }, onError: (error) => { showErrorNotification(notifications, error as AxiosError); + + // Allow user to proceed even if API fails + setCurrentStep(4); }, }, ); @@ -174,10 +177,16 @@ function OnboardingQuestionaire(): JSX.Element { ? (orgDetails?.otherTool as string) : (orgDetails?.observabilityTool as string), where_did_you_discover_signoz: signozDetails?.discoverSignoz as string, - reasons_for_interest_in_signoz: - signozDetails?.interestInSignoz === 'Others' - ? (signozDetails?.otherInterestInSignoz as string) - : (signozDetails?.interestInSignoz as string), + reasons_for_interest_in_signoz: signozDetails?.interestInSignoz?.includes( + 'Others', + ) + ? ([ + ...(signozDetails?.interestInSignoz?.filter( + (item) => item !== 'Others', + ) || []), + signozDetails?.otherInterestInSignoz, + ] as string[]) + : (signozDetails?.interestInSignoz as string[]), logs_scale_per_day_in_gb: optimiseSignozDetails?.logsPerDay as number, number_of_hosts: optimiseSignozDetails?.hostsPerDay as number, number_of_services: optimiseSignozDetails?.services as number, diff --git a/frontend/src/types/api/onboarding/types.ts b/frontend/src/types/api/onboarding/types.ts index 9827f7fa415a..f33b8d679b2e 100644 --- a/frontend/src/types/api/onboarding/types.ts +++ b/frontend/src/types/api/onboarding/types.ts @@ -1,5 +1,5 @@ export interface UpdateProfileProps { - reasons_for_interest_in_signoz: string; + reasons_for_interest_in_signoz: string[]; uses_otel: boolean; has_existing_observability_tool: boolean; existing_observability_tool: string;