mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 01:45:53 +08:00
feat: maintain state and add log events
This commit is contained in:
parent
438cbcef87
commit
6664e1bc02
@ -1,23 +1,52 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
import '../OnboardingQuestionaire.styles.scss';
|
||||
|
||||
import { Button, Typography } from 'antd';
|
||||
import { ArrowLeft, ArrowRight } from 'lucide-react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Input, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { ArrowLeft, ArrowRight, CheckCircle } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
interface AboutSigNozQuestionsProps {
|
||||
signozDetails: any;
|
||||
setSignozDetails: (details: any) => void;
|
||||
onNext: () => void;
|
||||
onBack: () => void;
|
||||
}
|
||||
|
||||
const hearAboutSignozOptions: Record<string, string> = {
|
||||
blog: 'Blog',
|
||||
hackerNews: 'Hacker News',
|
||||
linkedin: 'LinkedIn',
|
||||
twitter: 'Twitter',
|
||||
reddit: 'Reddit',
|
||||
colleaguesFriends: 'Colleagues / Friends',
|
||||
};
|
||||
|
||||
const interestedInOptions: Record<string, string> = {
|
||||
savingCosts: 'Saving costs',
|
||||
otelNativeStack: 'Interested in Otel-native stack',
|
||||
allInOne: 'All in one',
|
||||
};
|
||||
|
||||
export function AboutSigNozQuestions({
|
||||
signozDetails,
|
||||
setSignozDetails,
|
||||
onNext,
|
||||
onBack,
|
||||
}: AboutSigNozQuestionsProps): JSX.Element {
|
||||
const [hearAboutSignoz, setHearAboutSignoz] = useState<string | null>(null);
|
||||
const [otherAboutSignoz, setOtherAboutSignoz] = useState<string>('');
|
||||
const [interestedSignoz, setInterestedSignoz] = useState<string | null>(null);
|
||||
const [otherInterest, setOtherInterest] = useState<string>('');
|
||||
const [hearAboutSignoz, setHearAboutSignoz] = useState<string | null>(
|
||||
signozDetails?.hearAboutSignoz || null,
|
||||
);
|
||||
const [otherAboutSignoz, setOtherAboutSignoz] = useState<string>(
|
||||
signozDetails?.otherAboutSignoz || '',
|
||||
);
|
||||
const [interestedSignoz, setInterestedSignoz] = useState<string | null>(
|
||||
signozDetails?.interestedSignoz || null,
|
||||
);
|
||||
const [otherInterest, setOtherInterest] = useState<string>(
|
||||
signozDetails?.otherInterest || '',
|
||||
);
|
||||
const [isNextDisabled, setIsNextDisabled] = useState<boolean>(true);
|
||||
|
||||
useEffect((): void => {
|
||||
@ -33,6 +62,42 @@ export function AboutSigNozQuestions({
|
||||
}
|
||||
}, [hearAboutSignoz, otherAboutSignoz, interestedSignoz, otherInterest]);
|
||||
|
||||
const handleOnNext = (): void => {
|
||||
setSignozDetails({
|
||||
hearAboutSignoz,
|
||||
otherAboutSignoz,
|
||||
interestedSignoz,
|
||||
otherInterest,
|
||||
});
|
||||
|
||||
logEvent('Onboarding: SigNoz Questions: Next', {
|
||||
hearAboutSignoz,
|
||||
otherAboutSignoz,
|
||||
interestedSignoz,
|
||||
otherInterest,
|
||||
});
|
||||
|
||||
onNext();
|
||||
};
|
||||
|
||||
const handleOnBack = (): void => {
|
||||
setSignozDetails({
|
||||
hearAboutSignoz,
|
||||
otherAboutSignoz,
|
||||
interestedSignoz,
|
||||
otherInterest,
|
||||
});
|
||||
|
||||
logEvent('Onboarding: SigNoz Questions: Back', {
|
||||
hearAboutSignoz,
|
||||
otherAboutSignoz,
|
||||
interestedSignoz,
|
||||
otherInterest,
|
||||
});
|
||||
|
||||
onBack();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="questions-container">
|
||||
<Typography.Title level={3} className="title">
|
||||
@ -46,78 +111,49 @@ export function AboutSigNozQuestions({
|
||||
<div className="questions-form">
|
||||
<div className="form-group">
|
||||
<div className="question">Where did you hear about SigNoz?</div>
|
||||
<div className="tool-grid">
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${hearAboutSignoz === 'Blog' ? 'active' : ''}`}
|
||||
onClick={(): void => setHearAboutSignoz('Blog')}
|
||||
>
|
||||
Blog
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
hearAboutSignoz === 'Hacker News' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setHearAboutSignoz('Hacker News')}
|
||||
>
|
||||
Hacker News
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
hearAboutSignoz === 'LinkedIn' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setHearAboutSignoz('LinkedIn')}
|
||||
>
|
||||
LinkedIn
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
hearAboutSignoz === 'Twitter' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setHearAboutSignoz('Twitter')}
|
||||
>
|
||||
Twitter
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
hearAboutSignoz === 'Reddit' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setHearAboutSignoz('Reddit')}
|
||||
>
|
||||
Reddit
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
hearAboutSignoz === 'colleagues/friends' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setHearAboutSignoz('colleagues/friends')}
|
||||
>
|
||||
Colleagues / Friends
|
||||
</button>
|
||||
<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' ? (
|
||||
<input
|
||||
<Input
|
||||
type="text"
|
||||
className="tool-button input-field"
|
||||
placeholder="Please specify where you heard about SigNoz"
|
||||
className="onboarding-questionaire-other-input"
|
||||
placeholder="Please specify your interest"
|
||||
value={otherAboutSignoz}
|
||||
autoFocus
|
||||
addonAfter={
|
||||
otherAboutSignoz !== '' ? (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
onChange={(e): void => setOtherAboutSignoz(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
<Button
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
hearAboutSignoz === 'Others' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setHearAboutSignoz('Others')}
|
||||
>
|
||||
Others
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@ -126,62 +162,56 @@ export function AboutSigNozQuestions({
|
||||
<div className="question">
|
||||
What are you interested in doing with SigNoz?
|
||||
</div>
|
||||
<div className="grid">
|
||||
<button
|
||||
type="button"
|
||||
className={`grid-button ${
|
||||
interestedSignoz === 'Saving costs' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setInterestedSignoz('Saving costs')}
|
||||
>
|
||||
Saving costs
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`grid-button ${
|
||||
interestedSignoz === 'Interested in Otel-native stack' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void =>
|
||||
setInterestedSignoz('Interested in Otel-native stack')
|
||||
}
|
||||
>
|
||||
Interested in Otel-native stack
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`grid-button ${
|
||||
interestedSignoz === 'All-in-one' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setInterestedSignoz('All-in-one')}
|
||||
>
|
||||
All-in-one
|
||||
</button>
|
||||
<div className="two-column-grid">
|
||||
{Object.keys(interestedInOptions).map((option: string) => (
|
||||
<Button
|
||||
key={option}
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
interestedSignoz === option ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setInterestedSignoz(option)}
|
||||
>
|
||||
{interestedInOptions[option]}
|
||||
{interestedSignoz === option && (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
)}
|
||||
</Button>
|
||||
))}
|
||||
|
||||
{interestedSignoz === 'Others' ? (
|
||||
<input
|
||||
<Input
|
||||
type="text"
|
||||
className="tool-button input-field"
|
||||
className="onboarding-questionaire-other-input"
|
||||
placeholder="Please specify your interest"
|
||||
value={otherInterest}
|
||||
autoFocus
|
||||
addonAfter={
|
||||
otherInterest !== '' ? (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
onChange={(e): void => setOtherInterest(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
<Button
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
interestedSignoz === 'Others' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setInterestedSignoz('Others')}
|
||||
>
|
||||
Others
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="next-prev-container">
|
||||
<Button type="default" className="next-button" onClick={onBack}>
|
||||
<Button type="default" className="next-button" onClick={handleOnBack}>
|
||||
<ArrowLeft size={14} />
|
||||
Back
|
||||
</Button>
|
||||
@ -189,7 +219,7 @@ export function AboutSigNozQuestions({
|
||||
<Button
|
||||
type="primary"
|
||||
className={`next-button ${isNextDisabled ? 'disabled' : ''}`}
|
||||
onClick={onNext}
|
||||
onClick={handleOnNext}
|
||||
disabled={isNextDisabled}
|
||||
>
|
||||
Next
|
||||
|
@ -1,7 +1,17 @@
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Input, Select, Typography } from 'antd';
|
||||
import { ArrowLeft, ArrowRight } from 'lucide-react';
|
||||
import {
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
CheckCircle,
|
||||
Plus,
|
||||
TriangleAlert,
|
||||
} from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface InviteTeamMembersProps {
|
||||
teamMembers: string[];
|
||||
setTeamMembers: (teamMembers: string[]) => void;
|
||||
onNext: () => void;
|
||||
onBack: () => void;
|
||||
}
|
||||
@ -15,9 +25,39 @@ const userRolesOptions = (
|
||||
);
|
||||
|
||||
function InviteTeamMembers({
|
||||
teamMembers,
|
||||
setTeamMembers,
|
||||
onNext,
|
||||
onBack,
|
||||
}: InviteTeamMembersProps): JSX.Element {
|
||||
const [teamMembersToInvite, setTeamMembersToInvite] = useState<string[]>(
|
||||
teamMembers || [''],
|
||||
);
|
||||
|
||||
const handleAddTeamMember = (): void => {
|
||||
setTeamMembersToInvite([...teamMembersToInvite, '']);
|
||||
};
|
||||
|
||||
const handleNext = (): void => {
|
||||
console.log(teamMembersToInvite);
|
||||
setTeamMembers(teamMembersToInvite);
|
||||
onNext();
|
||||
};
|
||||
|
||||
const handleOnChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
index: number,
|
||||
): void => {
|
||||
const newTeamMembers = [...teamMembersToInvite];
|
||||
newTeamMembers[index] = e.target.value;
|
||||
setTeamMembersToInvite(newTeamMembers);
|
||||
};
|
||||
|
||||
const isValidEmail = (email: string): boolean => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="questions-container">
|
||||
<Typography.Title level={3} className="title">
|
||||
@ -39,20 +79,44 @@ function InviteTeamMembers({
|
||||
</div>
|
||||
|
||||
<div className="invite-team-members-container">
|
||||
<Input
|
||||
addonAfter={userRolesOptions}
|
||||
placeholder="your-teammate@org.com"
|
||||
/>
|
||||
{teamMembersToInvite.map((member, index) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<div className="team-member-container" key={`${member}-${index}`}>
|
||||
<Input
|
||||
addonBefore={userRolesOptions}
|
||||
addonAfter={
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
member.length > 0 ? (
|
||||
isValidEmail(member) ? (
|
||||
<CheckCircle size={14} color={Color.BG_FOREST_500} />
|
||||
) : (
|
||||
<TriangleAlert size={14} color={Color.BG_SIENNA_500} />
|
||||
)
|
||||
) : null
|
||||
}
|
||||
placeholder="your-teammate@org.com"
|
||||
value={member}
|
||||
type="email"
|
||||
required
|
||||
autoFocus
|
||||
autoComplete="off"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>): void =>
|
||||
handleOnChange(e, index)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Input
|
||||
addonAfter={userRolesOptions}
|
||||
placeholder="your-teammate@org.com"
|
||||
/>
|
||||
|
||||
<Input
|
||||
addonAfter={userRolesOptions}
|
||||
placeholder="your-teammate@org.com"
|
||||
/>
|
||||
<div className="invite-team-members-add-another-member-container">
|
||||
<Button
|
||||
type="primary"
|
||||
className="add-another-member-button"
|
||||
icon={<Plus size={14} />}
|
||||
onClick={handleAddTeamMember}
|
||||
>
|
||||
Member
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -63,7 +127,7 @@ function InviteTeamMembers({
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<Button type="primary" className="next-button" onClick={onNext}>
|
||||
<Button type="primary" className="next-button" onClick={handleNext}>
|
||||
Send Invites
|
||||
<ArrowRight size={14} />
|
||||
</Button>
|
||||
|
@ -216,6 +216,7 @@
|
||||
transition: background-color 0.3s ease;
|
||||
min-width: 258px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.radio-button.active,
|
||||
@ -228,6 +229,61 @@
|
||||
background: rgba(78, 116, 248, 0.2);
|
||||
}
|
||||
|
||||
.two-column-grid {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr; /* Two equal columns */
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.onboarding-questionaire-button,
|
||||
.add-another-member-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--Greyscale-Slate-400, #1d212d);
|
||||
background: var(--Ink-300, #16181d);
|
||||
color: var(--Vanilla-400, var(--Greyscale-Vanilla-400, #c0c1c3));
|
||||
box-shadow: none;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
text-align: left;
|
||||
font-weight: 400;
|
||||
transition: background-color 0.3s ease;
|
||||
cursor: pointer;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid rgba(78, 116, 248, 0.4);
|
||||
background: rgba(78, 116, 248, 0.2);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border: 1px solid rgba(78, 116, 248, 0.4);
|
||||
background: rgba(78, 116, 248, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.add-another-member-button {
|
||||
font-size: 12px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.onboarding-questionaire-other-input {
|
||||
.ant-input-group {
|
||||
.ant-input {
|
||||
border-top-right-radius: 0px !important;
|
||||
border-bottom-right-radius: 0px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tool-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
@ -275,4 +331,12 @@
|
||||
padding: 12px 24px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.invite-team-members-add-another-member-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
import { Button, Slider, SliderSingleProps, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { ArrowLeft, ArrowRight, Minus } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface OptimiseSignozNeedsProps {
|
||||
optimiseSignozDetails: Record<string, number> | null;
|
||||
setOptimiseSignozDetails: (details: Record<string, number> | null) => void;
|
||||
onNext: () => void;
|
||||
onBack: () => void;
|
||||
}
|
||||
@ -32,9 +36,69 @@ const serviceMarks: SliderSingleProps['marks'] = {
|
||||
};
|
||||
|
||||
function OptimiseSignozNeeds({
|
||||
optimiseSignozDetails,
|
||||
setOptimiseSignozDetails,
|
||||
onNext,
|
||||
onBack,
|
||||
}: OptimiseSignozNeedsProps): JSX.Element {
|
||||
const [logsPerDay, setLogsPerDay] = useState<number>(
|
||||
optimiseSignozDetails?.logsPerDay || 25,
|
||||
);
|
||||
const [hostsPerDay, setHostsPerDay] = useState<number>(
|
||||
optimiseSignozDetails?.hostsPerDay || 40,
|
||||
);
|
||||
const [services, setServices] = useState<number>(
|
||||
optimiseSignozDetails?.services || 10,
|
||||
);
|
||||
|
||||
const handleOnNext = (): void => {
|
||||
setOptimiseSignozDetails({
|
||||
logsPerDay,
|
||||
hostsPerDay,
|
||||
services,
|
||||
});
|
||||
|
||||
logEvent('Onboarding: Optimise SigNoz Needs: Next', {
|
||||
logsPerDay,
|
||||
hostsPerDay,
|
||||
services,
|
||||
});
|
||||
|
||||
onNext();
|
||||
};
|
||||
|
||||
const handleOnBack = (): void => {
|
||||
setOptimiseSignozDetails({
|
||||
logsPerDay,
|
||||
hostsPerDay,
|
||||
services,
|
||||
});
|
||||
|
||||
logEvent('Onboarding: Optimise SigNoz Needs: Back', {
|
||||
logsPerDay,
|
||||
hostsPerDay,
|
||||
services,
|
||||
});
|
||||
|
||||
onBack();
|
||||
};
|
||||
|
||||
const handleWillDoLater = (): void => {
|
||||
setOptimiseSignozDetails({
|
||||
logsPerDay: 0,
|
||||
hostsPerDay: 0,
|
||||
services: 0,
|
||||
});
|
||||
|
||||
logEvent('Onboarding: Optimise SigNoz Needs: Will do later', {
|
||||
logsPerDay: 0,
|
||||
hostsPerDay: 0,
|
||||
services: 0,
|
||||
});
|
||||
|
||||
onNext();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="questions-container">
|
||||
<Typography.Title level={3} className="title">
|
||||
@ -57,7 +121,8 @@ function OptimiseSignozNeeds({
|
||||
<div className="slider-container">
|
||||
<Slider
|
||||
marks={logMarks}
|
||||
defaultValue={25}
|
||||
defaultValue={logsPerDay}
|
||||
onChange={(value): void => setLogsPerDay(value)}
|
||||
styles={{
|
||||
track: {
|
||||
background: '#4E74F8',
|
||||
@ -69,12 +134,13 @@ function OptimiseSignozNeeds({
|
||||
|
||||
<div className="form-group">
|
||||
<label className="question" htmlFor="organisationName">
|
||||
Metrics <Minus size={14} /> Number of Hosts / Day
|
||||
Metrics <Minus size={14} /> Number of Hosts
|
||||
</label>
|
||||
<div className="slider-container">
|
||||
<Slider
|
||||
marks={hostMarks}
|
||||
defaultValue={40}
|
||||
defaultValue={hostsPerDay}
|
||||
onChange={(value): void => setHostsPerDay(value)}
|
||||
styles={{
|
||||
track: {
|
||||
background: '#4E74F8',
|
||||
@ -91,7 +157,8 @@ function OptimiseSignozNeeds({
|
||||
<div className="slider-container">
|
||||
<Slider
|
||||
marks={serviceMarks}
|
||||
defaultValue={10}
|
||||
defaultValue={services}
|
||||
onChange={(value): void => setServices(value)}
|
||||
styles={{
|
||||
track: {
|
||||
background: '#4E74F8',
|
||||
@ -103,19 +170,19 @@ function OptimiseSignozNeeds({
|
||||
</div>
|
||||
|
||||
<div className="next-prev-container">
|
||||
<Button type="default" className="next-button" onClick={onBack}>
|
||||
<Button type="default" className="next-button" onClick={handleOnBack}>
|
||||
<ArrowLeft size={14} />
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<Button type="primary" className="next-button" onClick={onNext}>
|
||||
<Button type="primary" className="next-button" onClick={handleOnNext}>
|
||||
Next
|
||||
<ArrowRight size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="do-later-container">
|
||||
<Button type="link" onClick={onNext}>
|
||||
<Button type="link" onClick={handleWillDoLater}>
|
||||
I'll do this later
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -1,27 +1,58 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
import '../OnboardingQuestionaire.styles.scss';
|
||||
|
||||
import { Button, Typography } from 'antd';
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Input, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { ArrowRight, CheckCircle } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
|
||||
interface OrgQuestionsProps {
|
||||
orgDetails: any;
|
||||
setOrgDetails: (details: any) => void;
|
||||
onNext: () => void;
|
||||
}
|
||||
|
||||
function OrgQuestions({ onNext }: OrgQuestionsProps): JSX.Element {
|
||||
const [organisationName, setOrganisationName] = useState<string>('');
|
||||
const observabilityTools = [
|
||||
'AWS Cloudwatch',
|
||||
'DataDog',
|
||||
'New Relic',
|
||||
'Grafana / Prometheus',
|
||||
'Azure App Monitor',
|
||||
'GCP-native o11y tools',
|
||||
'Honeycomb',
|
||||
];
|
||||
|
||||
const o11yFamiliarityOptions: Record<string, string> = {
|
||||
new: "I'm completely new",
|
||||
builtStack: "I've built a stack before",
|
||||
experienced: 'I have some experience',
|
||||
dontKnow: "I don't know what it is",
|
||||
};
|
||||
|
||||
function OrgQuestions({
|
||||
orgDetails,
|
||||
setOrgDetails,
|
||||
onNext,
|
||||
}: OrgQuestionsProps): JSX.Element {
|
||||
const [organisationName, setOrganisationName] = useState<string>(
|
||||
orgDetails?.organisationName || '',
|
||||
);
|
||||
const [usesObservability, setUsesObservability] = useState<boolean | null>(
|
||||
null,
|
||||
orgDetails?.usesObservability || null,
|
||||
);
|
||||
const [observabilityTool, setObservabilityTool] = useState<string | null>(
|
||||
null,
|
||||
orgDetails?.observabilityTool || null,
|
||||
);
|
||||
const [otherTool, setOtherTool] = useState<string>(
|
||||
orgDetails?.otherTool || '',
|
||||
);
|
||||
const [familiarity, setFamiliarity] = useState<string | null>(
|
||||
orgDetails?.familiarity || null,
|
||||
);
|
||||
const [otherTool, setOtherTool] = useState<string>('');
|
||||
const [familiarity, setFamiliarity] = useState<string | null>(null);
|
||||
const [isNextDisabled, setIsNextDisabled] = useState<boolean>(true);
|
||||
|
||||
const { user } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
@ -45,6 +76,26 @@ function OrgQuestions({ onNext }: OrgQuestionsProps): JSX.Element {
|
||||
otherTool,
|
||||
]);
|
||||
|
||||
const handleOnNext = (): void => {
|
||||
setOrgDetails({
|
||||
organisationName,
|
||||
usesObservability,
|
||||
observabilityTool,
|
||||
otherTool,
|
||||
familiarity,
|
||||
});
|
||||
|
||||
logEvent('Onboarding: Org Questions: Next', {
|
||||
organisationName,
|
||||
usesObservability,
|
||||
observabilityTool,
|
||||
otherTool,
|
||||
familiarity,
|
||||
});
|
||||
|
||||
onNext();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="questions-container">
|
||||
<Typography.Title level={3} className="title">
|
||||
@ -77,20 +128,25 @@ function OrgQuestions({ onNext }: OrgQuestionsProps): JSX.Element {
|
||||
Do you currently use any observability/monitoring tool?
|
||||
</label>
|
||||
|
||||
<div className="radio-group">
|
||||
<button
|
||||
type="button"
|
||||
<div className="two-column-grid">
|
||||
<Button
|
||||
type="primary"
|
||||
name="usesObservability"
|
||||
className={`radio-button ${usesObservability === true ? 'active' : ''}`}
|
||||
className={`onboarding-questionaire-button ${
|
||||
usesObservability === true ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => {
|
||||
setUsesObservability(true);
|
||||
}}
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`radio-button ${
|
||||
Yes{' '}
|
||||
{usesObservability === true && (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
usesObservability === false ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => {
|
||||
@ -99,8 +155,11 @@ function OrgQuestions({ onNext }: OrgQuestionsProps): JSX.Element {
|
||||
setOtherTool('');
|
||||
}}
|
||||
>
|
||||
No
|
||||
</button>
|
||||
No{' '}
|
||||
{usesObservability === false && (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -109,83 +168,44 @@ function OrgQuestions({ onNext }: OrgQuestionsProps): JSX.Element {
|
||||
<label className="question" htmlFor="observabilityTool">
|
||||
Which observability tool do you currently use?
|
||||
</label>
|
||||
<div className="tool-grid">
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
observabilityTool === 'AWS Cloudwatch' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool('AWS Cloudwatch')}
|
||||
>
|
||||
AWS Cloudwatch
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
observabilityTool === 'DataDog' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool('DataDog')}
|
||||
>
|
||||
DataDog
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
observabilityTool === 'New Relic' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool('New Relic')}
|
||||
>
|
||||
New Relic
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
observabilityTool === 'Grafana / Prometheus' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool('Grafana / Prometheus')}
|
||||
>
|
||||
Grafana / Prometheus
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
observabilityTool === 'Azure App Monitor' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool('Azure App Monitor')}
|
||||
>
|
||||
Azure App Monitor
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
observabilityTool === 'GCP-native o11y tools' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool('GCP-native o11y tools')}
|
||||
>
|
||||
GCP-native o11y tools
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
observabilityTool === 'Honeycomb' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool('Honeycomb')}
|
||||
>
|
||||
Honeycomb
|
||||
</button>
|
||||
<div className="two-column-grid">
|
||||
{observabilityTools.map((tool) => (
|
||||
<Button
|
||||
key={tool}
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
observabilityTool === tool ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool(tool)}
|
||||
>
|
||||
{tool}
|
||||
|
||||
{observabilityTool === tool && (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
)}
|
||||
</Button>
|
||||
))}
|
||||
|
||||
{observabilityTool === 'Others' ? (
|
||||
<input
|
||||
<Input
|
||||
type="text"
|
||||
className="tool-button input-field"
|
||||
className="onboarding-questionaire-other-input"
|
||||
placeholder="Please specify the tool"
|
||||
value={otherTool}
|
||||
autoFocus
|
||||
addonAfter={
|
||||
otherTool !== '' ? (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
onChange={(e): void => setOtherTool(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className={`tool-button ${
|
||||
className={`onboarding-questionaire-button ${
|
||||
observabilityTool === 'Others' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setObservabilityTool('Others')}
|
||||
@ -201,39 +221,22 @@ function OrgQuestions({ onNext }: OrgQuestionsProps): JSX.Element {
|
||||
<div className="question">
|
||||
Are you familiar with observability (o11y)?
|
||||
</div>
|
||||
<div className="grid">
|
||||
<button
|
||||
type="button"
|
||||
className={`grid-button ${familiarity === 'new' ? 'active' : ''}`}
|
||||
onClick={(): void => setFamiliarity('new')}
|
||||
>
|
||||
I'm completely new
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`grid-button ${
|
||||
familiarity === 'built-stack' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setFamiliarity('built-stack')}
|
||||
>
|
||||
I've built a stack before
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`grid-button ${
|
||||
familiarity === 'experienced' ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setFamiliarity('experienced')}
|
||||
>
|
||||
I have some experience
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`grid-button ${familiarity === 'dont-know' ? 'active' : ''}`}
|
||||
onClick={(): void => setFamiliarity('dont-know')}
|
||||
>
|
||||
I don't know what it is
|
||||
</button>
|
||||
<div className="two-column-grid">
|
||||
{Object.keys(o11yFamiliarityOptions).map((option: string) => (
|
||||
<Button
|
||||
key={option}
|
||||
type="primary"
|
||||
className={`onboarding-questionaire-button ${
|
||||
familiarity === option ? 'active' : ''
|
||||
}`}
|
||||
onClick={(): void => setFamiliarity(option)}
|
||||
>
|
||||
{o11yFamiliarityOptions[option]}
|
||||
{familiarity === option && (
|
||||
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
||||
)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -242,7 +245,7 @@ function OrgQuestions({ onNext }: OrgQuestionsProps): JSX.Element {
|
||||
<Button
|
||||
type="primary"
|
||||
className={`next-button ${isNextDisabled ? 'disabled' : ''}`}
|
||||
onClick={onNext}
|
||||
onClick={handleOnNext}
|
||||
disabled={isNextDisabled}
|
||||
>
|
||||
Next
|
||||
|
@ -12,6 +12,20 @@ import OrgQuestions from './OrgQuestions/OrgQuestions';
|
||||
function OnboardingQuestionaire(): JSX.Element {
|
||||
const [currentStep, setCurrentStep] = useState<number>(1);
|
||||
|
||||
const [orgDetails, setOrgDetails] = useState<Record<string, string> | null>(
|
||||
null,
|
||||
);
|
||||
const [signozDetails, setSignozDetails] = useState<Record<
|
||||
string,
|
||||
string
|
||||
> | null>(null);
|
||||
const [optimiseSignozDetails, setOptimiseSignozDetails] = useState<Record<
|
||||
string,
|
||||
number
|
||||
> | null>(null);
|
||||
|
||||
const [teamMembers, setTeamMembers] = useState<string[]>(['']);
|
||||
|
||||
return (
|
||||
<div className="onboarding-questionaire-container">
|
||||
<div className="onboarding-questionaire-header">
|
||||
@ -20,11 +34,17 @@ function OnboardingQuestionaire(): JSX.Element {
|
||||
|
||||
<div className="onboarding-questionaire-content">
|
||||
{currentStep === 1 && (
|
||||
<OrgQuestions onNext={(): void => setCurrentStep(2)} />
|
||||
<OrgQuestions
|
||||
orgDetails={orgDetails}
|
||||
setOrgDetails={setOrgDetails}
|
||||
onNext={(): void => setCurrentStep(2)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{currentStep === 2 && (
|
||||
<AboutSigNozQuestions
|
||||
signozDetails={signozDetails}
|
||||
setSignozDetails={setSignozDetails}
|
||||
onBack={(): void => setCurrentStep(1)}
|
||||
onNext={(): void => setCurrentStep(3)}
|
||||
/>
|
||||
@ -32,6 +52,8 @@ function OnboardingQuestionaire(): JSX.Element {
|
||||
|
||||
{currentStep === 3 && (
|
||||
<OptimiseSignozNeeds
|
||||
optimiseSignozDetails={optimiseSignozDetails}
|
||||
setOptimiseSignozDetails={setOptimiseSignozDetails}
|
||||
onBack={(): void => setCurrentStep(2)}
|
||||
onNext={(): void => setCurrentStep(4)}
|
||||
/>
|
||||
@ -39,6 +61,8 @@ function OnboardingQuestionaire(): JSX.Element {
|
||||
|
||||
{currentStep === 4 && (
|
||||
<InviteTeamMembers
|
||||
teamMembers={teamMembers}
|
||||
setTeamMembers={setTeamMembers}
|
||||
onBack={(): void => setCurrentStep(3)}
|
||||
onNext={(): void => setCurrentStep(5)}
|
||||
/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user