mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 15:36:48 +00:00
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
This commit is contained in:
parent
b2dc2790d8
commit
73ff89a80a
@ -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<string, string> = {
|
||||
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<string | null>(
|
||||
signozDetails?.interestInSignoz || null,
|
||||
const [interestInSignoz, setInterestInSignoz] = useState<string[]>(
|
||||
signozDetails?.interestInSignoz || [],
|
||||
);
|
||||
const [otherInterestInSignoz, setOtherInterestInSignoz] = useState<string>(
|
||||
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({
|
||||
|
||||
<div className="form-group">
|
||||
<div className="question">What got you interested in SigNoz?</div>
|
||||
<div className="two-column-grid">
|
||||
<div className="checkbox-grid">
|
||||
{Object.keys(interestedInOptions).map((option: string) => (
|
||||
<Button
|
||||
key={option}
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
interestInSignoz === option ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setInterestInSignoz(option)}
|
||||
>
|
||||
{interestedInOptions[option]}
|
||||
{interestInSignoz === option && (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
)}
|
||||
</Button>
|
||||
<div key={option} className="checkbox-item">
|
||||
<Checkbox
|
||||
checked={interestInSignoz.includes(option)}
|
||||
onChange={(e): void => handleInterestChange(option, e.target.checked)}
|
||||
>
|
||||
{interestedInOptions[option]}
|
||||
</Checkbox>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{interestInSignoz === 'Others' ? (
|
||||
<Input
|
||||
type="text"
|
||||
className="onboarding-questionaire-other-input"
|
||||
placeholder="Please specify your interest"
|
||||
value={otherInterestInSignoz}
|
||||
autoFocus
|
||||
addonAfter={
|
||||
otherInterestInSignoz !== '' ? (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
) : (
|
||||
''
|
||||
)
|
||||
<div className="checkbox-item">
|
||||
<Checkbox
|
||||
checked={interestInSignoz.includes('Others')}
|
||||
onChange={(e): void =>
|
||||
handleInterestChange('Others', e.target.checked)
|
||||
}
|
||||
onChange={(e): void => setOtherInterestInSignoz(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
interestInSignoz === 'Others' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setInterestInSignoz('Others')}
|
||||
>
|
||||
Others
|
||||
</Button>
|
||||
)}
|
||||
</Checkbox>
|
||||
{interestInSignoz.includes('Others') && (
|
||||
<Input
|
||||
type="text"
|
||||
className="onboarding-questionaire-other-input"
|
||||
placeholder="Please specify your interest"
|
||||
value={otherInterestInSignoz}
|
||||
autoFocus
|
||||
addonAfter={
|
||||
otherInterestInSignoz !== '' ? (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
onChange={(e): void => setOtherInterestInSignoz(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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<string>(
|
||||
orgDetails?.organisationName || '',
|
||||
);
|
||||
const [usesObservability, setUsesObservability] = useState<boolean | null>(
|
||||
orgDetails?.usesObservability || null,
|
||||
);
|
||||
const [observabilityTool, setObservabilityTool] = useState<string | null>(
|
||||
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({
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="question" htmlFor="usesObservability">
|
||||
Do you currently use any observability/monitoring tool?
|
||||
<label className="question" htmlFor="observabilityTool">
|
||||
Which observability tool do you currently use?
|
||||
</label>
|
||||
|
||||
<div className="two-column-grid">
|
||||
<Button
|
||||
type="primary"
|
||||
name="usesObservability"
|
||||
className={`onboarding-questionaire-button ${
|
||||
usesObservability === true ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => {
|
||||
setUsesObservability(true);
|
||||
}}
|
||||
>
|
||||
Yes{' '}
|
||||
{usesObservability === true && (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
usesObservability === false ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => {
|
||||
setUsesObservability(false);
|
||||
setObservabilityTool(null);
|
||||
setOtherTool('');
|
||||
}}
|
||||
>
|
||||
No{' '}
|
||||
{usesObservability === false && (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
)}
|
||||
</Button>
|
||||
{Object.keys(observabilityTools).map((tool) => (
|
||||
<Button
|
||||
key={tool}
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
observabilityTool === tool ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool(tool)}
|
||||
>
|
||||
{observabilityTools[tool as keyof typeof observabilityTools]}
|
||||
|
||||
{observabilityTool === tool && (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
)}
|
||||
</Button>
|
||||
))}
|
||||
|
||||
{observabilityTool === 'Others' ? (
|
||||
<Input
|
||||
type="text"
|
||||
className="onboarding-questionaire-other-input"
|
||||
placeholder="Please specify the tool"
|
||||
value={otherTool || ''}
|
||||
autoFocus
|
||||
addonAfter={
|
||||
otherTool && otherTool !== '' ? (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
onChange={(e): void => setOtherTool(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
observabilityTool === 'Others' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool('Others')}
|
||||
>
|
||||
Others
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{usesObservability && (
|
||||
<div className="form-group">
|
||||
<label className="question" htmlFor="observabilityTool">
|
||||
Which observability tool do you currently use?
|
||||
</label>
|
||||
<div className="two-column-grid">
|
||||
{Object.keys(observabilityTools).map((tool) => (
|
||||
<Button
|
||||
key={tool}
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
observabilityTool === tool ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool(tool)}
|
||||
>
|
||||
{observabilityTools[tool as keyof typeof observabilityTools]}
|
||||
|
||||
{observabilityTool === tool && (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
)}
|
||||
</Button>
|
||||
))}
|
||||
|
||||
{observabilityTool === 'Others' ? (
|
||||
<Input
|
||||
type="text"
|
||||
className="onboarding-questionaire-other-input"
|
||||
placeholder="Please specify the tool"
|
||||
value={otherTool || ''}
|
||||
autoFocus
|
||||
addonAfter={
|
||||
otherTool && otherTool !== '' ? (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
onChange={(e): void => setOtherTool(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
observabilityTool === 'Others' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool('Others')}
|
||||
>
|
||||
Others
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="form-group">
|
||||
<div className="question">Do you already use OpenTelemetry?</div>
|
||||
<div className="two-column-grid">
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user