feat(user-profile): update organisation onboarding questions (#8206)

* feat: update org onboarding questions

* feat: type updates

* chore(user-profile): update the onboarding questions

---------

Co-authored-by: Vikrant Gupta <vikrant@signoz.io>
This commit is contained in:
Yunus M 2025-06-23 18:57:05 +05:30 committed by GitHub
parent 133c0deaa8
commit ebcb172614
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 95 additions and 124 deletions

View File

@ -3,15 +3,15 @@ import '../OnboardingQuestionaire.styles.scss';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Button, Input, Typography } from 'antd'; import { Button, Input, Typography } from 'antd';
import TextArea from 'antd/lib/input/TextArea';
import logEvent from 'api/common/logEvent'; import logEvent from 'api/common/logEvent';
import { ArrowLeft, ArrowRight, CheckCircle } from 'lucide-react'; import { ArrowLeft, ArrowRight, CheckCircle } from 'lucide-react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
export interface SignozDetails { export interface SignozDetails {
hearAboutSignoz: string | null;
interestInSignoz: string | null; interestInSignoz: string | null;
otherInterestInSignoz: string | null; otherInterestInSignoz: string | null;
otherAboutSignoz: string | null; discoverSignoz: string | null;
} }
interface AboutSigNozQuestionsProps { interface AboutSigNozQuestionsProps {
@ -21,15 +21,6 @@ interface AboutSigNozQuestionsProps {
onBack: () => void; onBack: () => void;
} }
const hearAboutSignozOptions: Record<string, string> = {
search: 'Google / Search',
hackerNews: 'Hacker News',
linkedin: 'LinkedIn',
twitter: 'Twitter',
reddit: 'Reddit',
colleaguesFriends: 'Colleagues / Friends',
};
const interestedInOptions: Record<string, string> = { const interestedInOptions: Record<string, string> = {
savingCosts: 'Saving costs', savingCosts: 'Saving costs',
otelNativeStack: 'Interested in Otel-native stack', otelNativeStack: 'Interested in Otel-native stack',
@ -42,24 +33,20 @@ export function AboutSigNozQuestions({
onNext, onNext,
onBack, onBack,
}: AboutSigNozQuestionsProps): JSX.Element { }: AboutSigNozQuestionsProps): JSX.Element {
const [hearAboutSignoz, setHearAboutSignoz] = useState<string | null>(
signozDetails?.hearAboutSignoz || null,
);
const [otherAboutSignoz, setOtherAboutSignoz] = useState<string>(
signozDetails?.otherAboutSignoz || '',
);
const [interestInSignoz, setInterestInSignoz] = useState<string | null>( const [interestInSignoz, setInterestInSignoz] = useState<string | null>(
signozDetails?.interestInSignoz || null, signozDetails?.interestInSignoz || null,
); );
const [otherInterestInSignoz, setOtherInterestInSignoz] = useState<string>( const [otherInterestInSignoz, setOtherInterestInSignoz] = useState<string>(
signozDetails?.otherInterestInSignoz || '', signozDetails?.otherInterestInSignoz || '',
); );
const [discoverSignoz, setDiscoverSignoz] = useState<string>(
signozDetails?.discoverSignoz || '',
);
const [isNextDisabled, setIsNextDisabled] = useState<boolean>(true); const [isNextDisabled, setIsNextDisabled] = useState<boolean>(true);
useEffect((): void => { useEffect((): void => {
if ( if (
hearAboutSignoz !== null && discoverSignoz !== '' &&
(hearAboutSignoz !== 'Others' || otherAboutSignoz !== '') &&
interestInSignoz !== null && interestInSignoz !== null &&
(interestInSignoz !== 'Others' || otherInterestInSignoz !== '') (interestInSignoz !== 'Others' || otherInterestInSignoz !== '')
) { ) {
@ -67,24 +54,17 @@ export function AboutSigNozQuestions({
} else { } else {
setIsNextDisabled(true); setIsNextDisabled(true);
} }
}, [ }, [interestInSignoz, otherInterestInSignoz, discoverSignoz]);
hearAboutSignoz,
otherAboutSignoz,
interestInSignoz,
otherInterestInSignoz,
]);
const handleOnNext = (): void => { const handleOnNext = (): void => {
setSignozDetails({ setSignozDetails({
hearAboutSignoz, discoverSignoz,
otherAboutSignoz,
interestInSignoz, interestInSignoz,
otherInterestInSignoz, otherInterestInSignoz,
}); });
logEvent('Org Onboarding: Answered', { logEvent('Org Onboarding: Answered', {
hearAboutSignoz, discoverSignoz,
otherAboutSignoz,
interestInSignoz, interestInSignoz,
otherInterestInSignoz, otherInterestInSignoz,
}); });
@ -94,8 +74,7 @@ export function AboutSigNozQuestions({
const handleOnBack = (): void => { const handleOnBack = (): void => {
setSignozDetails({ setSignozDetails({
hearAboutSignoz, discoverSignoz,
otherAboutSignoz,
interestInSignoz, interestInSignoz,
otherInterestInSignoz, otherInterestInSignoz,
}); });
@ -115,52 +94,16 @@ export function AboutSigNozQuestions({
<div className="questions-form-container"> <div className="questions-form-container">
<div className="questions-form"> <div className="questions-form">
<div className="form-group"> <div className="form-group">
<div className="question">Where did you hear about SigNoz?</div> <div className="question">How did you first come across SigNoz?</div>
<div className="two-column-grid">
{Object.keys(hearAboutSignozOptions).map((option: string) => (
<Button
key={option}
type="primary"
className={`onboarding-questionaire-button ${
hearAboutSignoz === option ? 'active' : ''
}`}
onClick={(): void => setHearAboutSignoz(option)}
>
{hearAboutSignozOptions[option]}
{hearAboutSignoz === option && (
<CheckCircle size={12} color={Color.BG_FOREST_500} />
)}
</Button>
))}
{hearAboutSignoz === 'Others' ? ( <TextArea
<Input className="discover-signoz-input"
type="text" placeholder="e.g., Google Search, Hacker News, Reddit, a friend, ChatGPT, a blog post, a conference, etc."
className="onboarding-questionaire-other-input" value={discoverSignoz}
placeholder="How you got to know about us" autoFocus
value={otherAboutSignoz} rows={4}
autoFocus onChange={(e): void => setDiscoverSignoz(e.target.value)}
addonAfter={ />
otherAboutSignoz !== '' ? (
<CheckCircle size={12} color={Color.BG_FOREST_500} />
) : (
''
)
}
onChange={(e): void => setOtherAboutSignoz(e.target.value)}
/>
) : (
<Button
type="primary"
className={`onboarding-questionaire-button ${
hearAboutSignoz === 'Others' ? 'active' : ''
}`}
onClick={(): void => setHearAboutSignoz('Others')}
>
Others
</Button>
)}
</div>
</div> </div>
<div className="form-group"> <div className="form-group">

View File

@ -84,6 +84,23 @@
} }
} }
.discover-signoz-input {
width: 100%;
height: 100px;
resize: none;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
color: var(--bg-vanilla-100);
border-radius: 4px;
font-size: 14px;
padding: 12px;
&::placeholder {
color: var(--bg-vanilla-400);
opacity: 1;
}
}
&.invite-team-members-form { &.invite-team-members-form {
min-height: calc(420px - 24px); min-height: calc(420px - 24px);
max-height: calc(420px - 24px); max-height: calc(420px - 24px);
@ -123,7 +140,7 @@
height: 32px; height: 32px;
background: var(--Ink-300, #16181d); background: var(--Ink-300, #16181d);
border: 1px solid var(--bg-slate-400); border: 1px solid var(--Greyscale-Slate-400, #1d212d);
color: var(--bg-vanilla-400); color: var(--bg-vanilla-400);
} }
@ -445,6 +462,17 @@
} }
} }
.discover-signoz-input {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-100);
color: var(--text-ink-300);
&::placeholder {
color: var(--bg-slate-400);
opacity: 1;
}
}
&.invite-team-members-form { &.invite-team-members-form {
.invite-team-members-container { .invite-team-members-container {
max-height: 260px; max-height: 260px;

View File

@ -21,7 +21,7 @@ export interface OrgDetails {
usesObservability: boolean | null; usesObservability: boolean | null;
observabilityTool: string | null; observabilityTool: string | null;
otherTool: string | null; otherTool: string | null;
familiarity: string | null; usesOtel: boolean | null;
} }
interface OrgQuestionsProps { interface OrgQuestionsProps {
@ -40,13 +40,6 @@ const observabilityTools = {
Honeycomb: 'Honeycomb', Honeycomb: 'Honeycomb',
}; };
const o11yFamiliarityOptions: Record<string, string> = {
beginner: 'Beginner',
intermediate: 'Intermediate',
expert: 'Expert',
notFamiliar: "I'm not familiar with it",
};
function OrgQuestions({ function OrgQuestions({
currentOrgData, currentOrgData,
orgDetails, orgDetails,
@ -69,9 +62,6 @@ function OrgQuestions({
const [otherTool, setOtherTool] = useState<string>( const [otherTool, setOtherTool] = useState<string>(
orgDetails?.otherTool || '', orgDetails?.otherTool || '',
); );
const [familiarity, setFamiliarity] = useState<string | null>(
orgDetails?.familiarity || null,
);
const [isNextDisabled, setIsNextDisabled] = useState<boolean>(true); const [isNextDisabled, setIsNextDisabled] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
@ -80,6 +70,10 @@ function OrgQuestions({
const [isLoading, setIsLoading] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(false);
const [usesOtel, setUsesOtel] = useState<boolean | null>(
orgDetails?.usesOtel || null,
);
const handleOrgNameUpdate = async (): Promise<void> => { const handleOrgNameUpdate = async (): Promise<void> => {
/* Early bailout if orgData is not set or if the organisation name is not set or if the organisation name is empty or if the organisation name is the same as the one in the orgData */ /* Early bailout if orgData is not set or if the organisation name is not set or if the organisation name is empty or if the organisation name is the same as the one in the orgData */
if ( if (
@ -92,7 +86,7 @@ function OrgQuestions({
usesObservability, usesObservability,
observabilityTool, observabilityTool,
otherTool, otherTool,
familiarity, usesOtel,
}); });
onNext({ onNext({
@ -100,7 +94,7 @@ function OrgQuestions({
usesObservability, usesObservability,
observabilityTool, observabilityTool,
otherTool, otherTool,
familiarity, usesOtel,
}); });
return; return;
@ -123,7 +117,7 @@ function OrgQuestions({
usesObservability, usesObservability,
observabilityTool, observabilityTool,
otherTool, otherTool,
familiarity, usesOtel,
}); });
onNext({ onNext({
@ -131,7 +125,7 @@ function OrgQuestions({
usesObservability, usesObservability,
observabilityTool, observabilityTool,
otherTool, otherTool,
familiarity, usesOtel,
}); });
} else { } else {
logEvent('Org Onboarding: Org Name Update Failed', { logEvent('Org Onboarding: Org Name Update Failed', {
@ -177,7 +171,7 @@ function OrgQuestions({
useEffect(() => { useEffect(() => {
const isValidObservability = isValidUsesObservability(); const isValidObservability = isValidUsesObservability();
if (organisationName !== '' && familiarity !== null && isValidObservability) { if (organisationName !== '' && usesOtel !== null && isValidObservability) {
setIsNextDisabled(false); setIsNextDisabled(false);
} else { } else {
setIsNextDisabled(true); setIsNextDisabled(true);
@ -186,7 +180,7 @@ function OrgQuestions({
}, [ }, [
organisationName, organisationName,
usesObservability, usesObservability,
familiarity, usesOtel,
observabilityTool, observabilityTool,
otherTool, otherTool,
]); ]);
@ -317,25 +311,37 @@ function OrgQuestions({
)} )}
<div className="form-group"> <div className="form-group">
<div className="question"> <div className="question">Do you already use OpenTelemetry?</div>
Are you familiar with setting up observability (o11y)?
</div>
<div className="two-column-grid"> <div className="two-column-grid">
{Object.keys(o11yFamiliarityOptions).map((option: string) => ( <Button
<Button type="primary"
key={option} name="usesObservability"
type="primary" className={`onboarding-questionaire-button ${
className={`onboarding-questionaire-button ${ usesOtel === true ? 'active' : ''
familiarity === option ? 'active' : '' }`}
}`} onClick={(): void => {
onClick={(): void => setFamiliarity(option)} setUsesOtel(true);
> }}
{o11yFamiliarityOptions[option]} >
{familiarity === option && ( Yes{' '}
<CheckCircle size={12} color={Color.BG_FOREST_500} /> {usesOtel === true && (
)} <CheckCircle size={12} color={Color.BG_FOREST_500} />
</Button> )}
))} </Button>
<Button
type="primary"
className={`onboarding-questionaire-button ${
usesOtel === false ? 'active' : ''
}`}
onClick={(): void => {
setUsesOtel(false);
}}
>
No{' '}
{usesOtel === false && (
<CheckCircle size={12} color={Color.BG_FOREST_500} />
)}
</Button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -42,14 +42,13 @@ const INITIAL_ORG_DETAILS: OrgDetails = {
usesObservability: true, usesObservability: true,
observabilityTool: '', observabilityTool: '',
otherTool: '', otherTool: '',
familiarity: '', usesOtel: null,
}; };
const INITIAL_SIGNOZ_DETAILS: SignozDetails = { const INITIAL_SIGNOZ_DETAILS: SignozDetails = {
hearAboutSignoz: '',
interestInSignoz: '', interestInSignoz: '',
otherInterestInSignoz: '', otherInterestInSignoz: '',
otherAboutSignoz: '', discoverSignoz: '',
}; };
const INITIAL_OPTIMISE_SIGNOZ_DETAILS: OptimiseSignozDetails = { const INITIAL_OPTIMISE_SIGNOZ_DETAILS: OptimiseSignozDetails = {
@ -168,22 +167,17 @@ function OnboardingQuestionaire(): JSX.Element {
}); });
updateProfile({ updateProfile({
familiarity_with_observability: orgDetails?.familiarity as string, uses_otel: orgDetails?.usesOtel as boolean,
has_existing_observability_tool: orgDetails?.usesObservability as boolean, has_existing_observability_tool: orgDetails?.usesObservability as boolean,
existing_observability_tool: existing_observability_tool:
orgDetails?.observabilityTool === 'Others' orgDetails?.observabilityTool === 'Others'
? (orgDetails?.otherTool as string) ? (orgDetails?.otherTool as string)
: (orgDetails?.observabilityTool as string), : (orgDetails?.observabilityTool as string),
where_did_you_discover_signoz: signozDetails?.discoverSignoz as string,
reasons_for_interest_in_signoz: reasons_for_interest_in_signoz:
signozDetails?.interestInSignoz === 'Others' signozDetails?.interestInSignoz === 'Others'
? (signozDetails?.otherInterestInSignoz as string) ? (signozDetails?.otherInterestInSignoz as string)
: (signozDetails?.interestInSignoz as string), : (signozDetails?.interestInSignoz as string),
where_did_you_hear_about_signoz:
signozDetails?.hearAboutSignoz === 'Others'
? (signozDetails?.otherAboutSignoz as string)
: (signozDetails?.hearAboutSignoz as string),
logs_scale_per_day_in_gb: optimiseSignozDetails?.logsPerDay as number, logs_scale_per_day_in_gb: optimiseSignozDetails?.logsPerDay as number,
number_of_hosts: optimiseSignozDetails?.hostsPerDay as number, number_of_hosts: optimiseSignozDetails?.hostsPerDay as number,
number_of_services: optimiseSignozDetails?.services as number, number_of_services: optimiseSignozDetails?.services as number,

View File

@ -1,10 +1,10 @@
export interface UpdateProfileProps { export interface UpdateProfileProps {
reasons_for_interest_in_signoz: string; reasons_for_interest_in_signoz: string;
familiarity_with_observability: string; uses_otel: boolean;
has_existing_observability_tool: boolean; has_existing_observability_tool: boolean;
existing_observability_tool: string; existing_observability_tool: string;
logs_scale_per_day_in_gb: number; logs_scale_per_day_in_gb: number;
number_of_services: number; number_of_services: number;
number_of_hosts: number; number_of_hosts: number;
where_did_you_hear_about_signoz: string; where_did_you_discover_signoz: string;
} }