diff --git a/frontend/public/Logos/hippa.svg b/frontend/public/Logos/hippa.svg new file mode 100644 index 0000000000..2a1b74b818 --- /dev/null +++ b/frontend/public/Logos/hippa.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/Logos/signoz-brand-logo-new.svg b/frontend/public/Logos/signoz-brand-logo-new.svg new file mode 100644 index 0000000000..f57d3c5def --- /dev/null +++ b/frontend/public/Logos/signoz-brand-logo-new.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/Logos/soc2.svg b/frontend/public/Logos/soc2.svg new file mode 100644 index 0000000000..234321c95c --- /dev/null +++ b/frontend/public/Logos/soc2.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/fonts/Satoshi-Regular.woff2 b/frontend/public/fonts/Satoshi-Regular.woff2 new file mode 100644 index 0000000000..81c40ab08a Binary files /dev/null and b/frontend/public/fonts/Satoshi-Regular.woff2 differ diff --git a/frontend/src/AppRoutes/pageComponents.ts b/frontend/src/AppRoutes/pageComponents.ts index 0a7764149b..c267304e6b 100644 --- a/frontend/src/AppRoutes/pageComponents.ts +++ b/frontend/src/AppRoutes/pageComponents.ts @@ -66,6 +66,10 @@ export const Onboarding = Loadable( () => import(/* webpackChunkName: "Onboarding" */ 'pages/OnboardingPage'), ); +export const OnboardingV2 = Loadable( + () => import(/* webpackChunkName: "Onboarding" */ 'pages/OnboardingPageV2'), +); + export const DashboardPage = Loadable( () => import(/* webpackChunkName: "DashboardPage" */ 'pages/DashboardsListPage'), diff --git a/frontend/src/AppRoutes/routes.ts b/frontend/src/AppRoutes/routes.ts index 42ce00c0fb..fc59fef1e3 100644 --- a/frontend/src/AppRoutes/routes.ts +++ b/frontend/src/AppRoutes/routes.ts @@ -31,6 +31,7 @@ import { NewDashboardPage, OldLogsExplorer, Onboarding, + OnboardingV2, OrganizationSettings, PasswordReset, PipelinePage, @@ -68,6 +69,13 @@ const routes: AppRoutes[] = [ isPrivate: true, key: 'GET_STARTED', }, + { + path: ROUTES.GET_STARTED_V2, + exact: false, + component: OnboardingV2, + isPrivate: true, + key: 'GET_STARTED_V2', + }, { component: LogsIndexToFields, path: ROUTES.LOGS_INDEX_FIELDS, diff --git a/frontend/src/constants/routes.ts b/frontend/src/constants/routes.ts index b4f43ee684..50d189f4a8 100644 --- a/frontend/src/constants/routes.ts +++ b/frontend/src/constants/routes.ts @@ -8,6 +8,7 @@ const ROUTES = { TRACE_DETAIL: '/trace/:id', TRACES_EXPLORER: '/traces-explorer', GET_STARTED: '/get-started', + GET_STARTED_V2: '/get-started-v2', GET_STARTED_APPLICATION_MONITORING: '/get-started/application-monitoring', GET_STARTED_LOGS_MANAGEMENT: '/get-started/logs-management', GET_STARTED_INFRASTRUCTURE_MONITORING: diff --git a/frontend/src/container/AppLayout/AppLayout.styles.scss b/frontend/src/container/AppLayout/AppLayout.styles.scss index 2ae1531c79..f939583e5f 100644 --- a/frontend/src/container/AppLayout/AppLayout.styles.scss +++ b/frontend/src/container/AppLayout/AppLayout.styles.scss @@ -7,6 +7,8 @@ width: calc(100% - 64px); z-index: 0; + margin: 0 auto; + .content-container { position: relative; margin: 0 1rem; diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 4cf2e0f5bb..706c02957d 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -214,6 +214,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element { const pageTitle = t(routeKey); const renderFullScreen = pathname === ROUTES.GET_STARTED || + pathname === ROUTES.GET_STARTED_V2 || + pathname === ROUTES.WORKSPACE_LOCKED || pathname === ROUTES.GET_STARTED_APPLICATION_MONITORING || pathname === ROUTES.GET_STARTED_INFRASTRUCTURE_MONITORING || pathname === ROUTES.GET_STARTED_LOGS_MANAGEMENT || diff --git a/frontend/src/container/OnboardingQuestionaire/AboutSigNozQuestions/AboutSigNozQuestions.tsx b/frontend/src/container/OnboardingQuestionaire/AboutSigNozQuestions/AboutSigNozQuestions.tsx new file mode 100644 index 0000000000..c573d552fe --- /dev/null +++ b/frontend/src/container/OnboardingQuestionaire/AboutSigNozQuestions/AboutSigNozQuestions.tsx @@ -0,0 +1,232 @@ +/* eslint-disable sonarjs/cognitive-complexity */ +import '../OnboardingQuestionaire.styles.scss'; + +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 = { + blog: 'Blog', + hackerNews: 'Hacker News', + linkedin: 'LinkedIn', + twitter: 'Twitter', + reddit: 'Reddit', + colleaguesFriends: 'Colleagues / Friends', +}; + +const interestedInOptions: Record = { + 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( + signozDetails?.hearAboutSignoz || null, + ); + const [otherAboutSignoz, setOtherAboutSignoz] = useState( + signozDetails?.otherAboutSignoz || '', + ); + const [interestedSignoz, setInterestedSignoz] = useState( + signozDetails?.interestedSignoz || null, + ); + const [otherInterest, setOtherInterest] = useState( + signozDetails?.otherInterest || '', + ); + const [isNextDisabled, setIsNextDisabled] = useState(true); + + useEffect((): void => { + if ( + hearAboutSignoz !== null && + (hearAboutSignoz !== 'Others' || otherAboutSignoz !== '') && + interestedSignoz !== null && + (interestedSignoz !== 'Others' || otherInterest !== '') + ) { + setIsNextDisabled(false); + } else { + setIsNextDisabled(true); + } + }, [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 ( +
+ + Tell Us About Your Interest in SigNoz + + + We'd love to know a little bit about you and your interest in SigNoz + + +
+
+
+
Where did you hear about SigNoz?
+
+ {Object.keys(hearAboutSignozOptions).map((option: string) => ( + + ))} + + {hearAboutSignoz === 'Others' ? ( + + ) : ( + '' + ) + } + onChange={(e): void => setOtherAboutSignoz(e.target.value)} + /> + ) : ( + + )} +
+
+ +
+
+ What are you interested in doing with SigNoz? +
+
+ {Object.keys(interestedInOptions).map((option: string) => ( + + ))} + + {interestedSignoz === 'Others' ? ( + + ) : ( + '' + ) + } + onChange={(e): void => setOtherInterest(e.target.value)} + /> + ) : ( + + )} +
+
+
+ +
+ + + +
+
+
+ ); +} diff --git a/frontend/src/container/OnboardingQuestionaire/InviteTeamMembers/InviteTeamMembers.tsx b/frontend/src/container/OnboardingQuestionaire/InviteTeamMembers/InviteTeamMembers.tsx new file mode 100644 index 0000000000..6eeb2c5e6f --- /dev/null +++ b/frontend/src/container/OnboardingQuestionaire/InviteTeamMembers/InviteTeamMembers.tsx @@ -0,0 +1,146 @@ +import { Color } from '@signozhq/design-tokens'; +import { Button, Input, Select, Typography } from 'antd'; +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; +} + +const userRolesOptions = ( + +); + +function InviteTeamMembers({ + teamMembers, + setTeamMembers, + onNext, + onBack, +}: InviteTeamMembersProps): JSX.Element { + const [teamMembersToInvite, setTeamMembersToInvite] = useState( + teamMembers || [''], + ); + + const handleAddTeamMember = (): void => { + setTeamMembersToInvite([...teamMembersToInvite, '']); + }; + + const handleNext = (): void => { + console.log(teamMembersToInvite); + setTeamMembers(teamMembersToInvite); + onNext(); + }; + + const handleOnChange = ( + e: React.ChangeEvent, + 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 ( +
+ + Observability made collaborative + + + The more your team uses SigNoz, the stronger your observability. Share + dashboards, collaborate on alerts, and troubleshoot faster together. + + +
+
+
+
+ Collaborate with your team +
+ Invite your team to the SigNoz workspace +
+
+ +
+ {teamMembersToInvite.map((member, index) => ( + // eslint-disable-next-line react/no-array-index-key +
+ 0 ? ( + isValidEmail(member) ? ( + + ) : ( + + ) + ) : null + } + placeholder="your-teammate@org.com" + value={member} + type="email" + required + autoFocus + autoComplete="off" + onChange={(e: React.ChangeEvent): void => + handleOnChange(e, index) + } + /> +
+ ))} +
+ +
+ +
+
+
+ +
+ + + +
+ +
+ +
+
+
+ ); +} + +export default InviteTeamMembers; diff --git a/frontend/src/container/OnboardingQuestionaire/OnboardingFooter/OnboardingFooter.styles.scss b/frontend/src/container/OnboardingQuestionaire/OnboardingFooter/OnboardingFooter.styles.scss new file mode 100644 index 0000000000..beddf2441e --- /dev/null +++ b/frontend/src/container/OnboardingQuestionaire/OnboardingFooter/OnboardingFooter.styles.scss @@ -0,0 +1,46 @@ +.footer-main-container { + display: flex; + justify-content: center; + box-sizing: border-box; +} + +.footer-container { + display: inline-flex; + height: 36px; + padding: 12px; + justify-content: center; + align-items: center; + gap: 32px; + flex-shrink: 0; + border-radius: 4px; + border: 1px solid var(--Greyscale-Slate-500, #161922); + background: var(--Ink-400, #121317); +} + +.footer-container .footer-content { + display: flex; + align-items: center; + gap: 10px; +} + +.footer-container .footer-link { + display: flex; + align-items: center; + gap: 6px; + color: #c0c1c3; +} + +.footer-container .footer-text { + color: var(--Vanilla-400, var(--Greyscale-Vanilla-400, #c0c1c3)); + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; + letter-spacing: 0.2px; +} + +.footer-container .footer-dot { + width: 4px; + height: 4px; + fill: var(--Greyscale-Slate-200, #2c3140); +} diff --git a/frontend/src/container/OnboardingQuestionaire/OnboardingFooter/OnboardingFooter.tsx b/frontend/src/container/OnboardingQuestionaire/OnboardingFooter/OnboardingFooter.tsx new file mode 100644 index 0000000000..25482e2436 --- /dev/null +++ b/frontend/src/container/OnboardingQuestionaire/OnboardingFooter/OnboardingFooter.tsx @@ -0,0 +1,55 @@ +import './OnboardingFooter.styles.scss'; + +import { ArrowUpRight, Dot } from 'lucide-react'; + +export function OnboardingFooter(): JSX.Element { + return ( +
+ +
+ ); +} diff --git a/frontend/src/container/OnboardingQuestionaire/OnboardingFooter/index.ts b/frontend/src/container/OnboardingQuestionaire/OnboardingFooter/index.ts new file mode 100644 index 0000000000..cc029cb359 --- /dev/null +++ b/frontend/src/container/OnboardingQuestionaire/OnboardingFooter/index.ts @@ -0,0 +1 @@ +export { OnboardingFooter } from './OnboardingFooter'; diff --git a/frontend/src/container/OnboardingQuestionaire/OnboardingHeader/OnboardingHeader.styles.scss b/frontend/src/container/OnboardingQuestionaire/OnboardingHeader/OnboardingHeader.styles.scss new file mode 100644 index 0000000000..0b2cecaac6 --- /dev/null +++ b/frontend/src/container/OnboardingQuestionaire/OnboardingHeader/OnboardingHeader.styles.scss @@ -0,0 +1,58 @@ +.header-container { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 0px; + + box-sizing: border-box; +} + +.header-container .logo-container { + display: flex; + align-items: center; + gap: 10px; +} + +.header-container .logo-container img { + height: 17.5px; + width: 17.5px; +} + +.header-container .logo-text { + font-family: 'Satoshi', sans-serif; + color: #fff; + font-size: 15.4px; + font-style: normal; + font-weight: 500; + line-height: 17.5px; +} + +.header-container .get-help-container { + display: flex; + width: 113px; + height: 32px; + padding: 6px; + justify-content: center; + align-items: center; + gap: 6px; + flex-shrink: 0; + border-radius: 2px; + border: 1px solid var(--Greyscale-Slate-400, #1d212d); + background: var(--Ink-300, #16181d); +} + +.header-container .get-help-container img { + width: 12px; + height: 12px; + flex-shrink: 0; +} + +.header-container .get-help-text { + color: var(--Vanilla-400, var(--Greyscale-Vanilla-400, #c0c1c3)); + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 10px; + letter-spacing: 0.12px; +} diff --git a/frontend/src/container/OnboardingQuestionaire/OnboardingHeader/OnboardingHeader.tsx b/frontend/src/container/OnboardingQuestionaire/OnboardingHeader/OnboardingHeader.tsx new file mode 100644 index 0000000000..76801d7620 --- /dev/null +++ b/frontend/src/container/OnboardingQuestionaire/OnboardingHeader/OnboardingHeader.tsx @@ -0,0 +1,19 @@ +import './OnboardingHeader.styles.scss'; + +import { Color } from '@signozhq/design-tokens'; +import { LifeBuoy } from 'lucide-react'; + +export function OnboardingHeader(): JSX.Element { + return ( +
+
+ SigNoz + SigNoz +
+
+ + Get Help +
+
+ ); +} diff --git a/frontend/src/container/OnboardingQuestionaire/OnboardingHeader/index.ts b/frontend/src/container/OnboardingQuestionaire/OnboardingHeader/index.ts new file mode 100644 index 0000000000..644a6d9b84 --- /dev/null +++ b/frontend/src/container/OnboardingQuestionaire/OnboardingHeader/index.ts @@ -0,0 +1 @@ +export { OnboardingHeader } from './OnboardingHeader'; diff --git a/frontend/src/container/OnboardingQuestionaire/OnboardingQuestionaire.styles.scss b/frontend/src/container/OnboardingQuestionaire/OnboardingQuestionaire.styles.scss new file mode 100644 index 0000000000..8be4dc608c --- /dev/null +++ b/frontend/src/container/OnboardingQuestionaire/OnboardingQuestionaire.styles.scss @@ -0,0 +1,342 @@ +.onboarding-questionaire-container { + width: 100%; + display: flex; + margin: 0 auto; + align-items: center; + flex-direction: column; + height: 100vh; + max-width: 1176px; + + .onboarding-questionaire-header { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + + height: 56px; + } + + .onboarding-questionaire-content { + height: calc(100vh - 56px - 60px); + width: 100%; + display: flex; + flex-direction: column; + overflow-y: auto; + + .questions-container { + color: var(--Vanilla-100, #fff); + font-family: Inter; + font-size: 24px; + font-style: normal; + font-weight: 600; + line-height: 32px; + max-width: 600px; + margin: 0 auto; + border-radius: 8px; + max-height: 100%; + } + + .title { + color: var(--Vanilla-100, #fff) !important; + font-size: 24px !important; + line-height: 32px !important; + margin-bottom: 8px !important; + } + + .sub-title { + color: var(--Vanilla-400, var(--Greyscale-Vanilla-400, #c0c1c3)) !important; + font-size: 14px !important; + font-style: normal; + font-weight: 400 !important; + line-height: 24px !important; + margin-top: 0px !important; + margin-bottom: 24px !important; + } + + .questions-form-container { + max-width: 600px; + width: 600px; + margin: 0 auto; + } + + .questions-form { + width: 100%; + display: flex; + min-height: 420px; + padding: 20px 24px 24px 24px; + flex-direction: column; + align-items: center; + gap: 24px; + border-radius: 4px; + border: 1px solid var(--Greyscale-Slate-500, #161922); + background: var(--Ink-400, #121317); + + .ant-form-item { + margin-bottom: 0px !important; + + .ant-form-item-label { + label { + color: var(--Vanilla-100, #fff) !important; + font-size: 13px !important; + font-weight: 500; + line-height: 20px; + } + } + } + } + + .invite-team-members-container { + display: flex; + width: 100%; + flex-direction: column; + gap: 12px; + + .ant-input-group { + width: 100%; + + .ant-input { + font-size: 12px; + + height: 32px; + background: var(--Ink-300, #16181d); + border: 1px solid var(--Greyscale-Slate-400, #1d212d); + color: var(--Vanilla-400, var(--Greyscale-Vanilla-400, #c0c1c3)); + } + + .ant-input-group-addon { + font-size: 11px; + height: 32px; + min-width: 80px; + background: var(--Ink-300, #16181d); + border: 1px solid var(--Greyscale-Slate-400, #1d212d); + border-left: 0px; + color: var(--Vanilla-400, var(--Greyscale-Vanilla-400, #c0c1c3)); + } + } + } + + .question-label { + color: var(--Vanilla-100, #fff); + font-size: 13px; + font-style: normal; + font-weight: 500; + line-height: 20px; + } + + .question-sub-label { + color: var(--Vanilla-400, var(--Greyscale-Vanilla-400, #c0c1c3)); + font-size: 11px; + font-style: normal; + font-weight: 400; + line-height: 16px; + } + + .next-prev-container { + display: flex; + justify-content: space-between; + align-items: center; + gap: 10px; + margin-bottom: 24px; + + .ant-btn { + flex: 1; + } + } + + .form-group { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + align-self: stretch; + } + + .slider-container { + width: 100%; + + .ant-slider .ant-slider-mark { + font-size: 10px; + } + } + + .do-later-container { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + margin-top: 24px; + } + + .question { + color: var(--Vanilla-100, #fff); + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 20px; + } + + input[type='text'] { + width: 100%; + padding: 12px; + border-radius: 2px; + font-size: 14px; + height: 40px; + border: 1px solid var(--Greyscale-Slate-400, #1d212d); + background: var(--Ink-300, #16181d); + color: #fff; + + &:focus-visible { + outline: none; + } + } + + .radio-group, + .grid, + .tool-grid { + display: flex; + align-items: flex-start; + align-content: flex-start; + gap: 10px; + align-self: stretch; + flex-wrap: wrap; + } + + .radio-button, + .grid-button, + .tool-button { + border-radius: 4px; + border: 1px solid var(--Greyscale-Slate-400, #1d212d); + background: var(--Ink-300, #16181d); + padding: 12px; + color: var(--Vanilla-400, var(--Greyscale-Vanilla-400, #c0c1c3)); + font-size: 14px; + font-style: normal; + text-align: left; + font-weight: 400; + transition: background-color 0.3s ease; + min-width: 258px; + cursor: pointer; + box-sizing: border-box; + } + + .radio-button.active, + .grid-button.active, + .tool-button.active, + .radio-button:hover, + .grid-button:hover, + .tool-button:hover { + border: 1px solid rgba(78, 116, 248, 0.4); + 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); + } + + .input-field { + flex: 0; + padding: 12px; + border: 1px solid var(--Greyscale-Slate-400, #1d212d); + background: var(--Ink-300, #16181d); + color: #fff; + border-radius: 4px; + font-size: 14px; + min-width: 258px; + } + + .next-button { + display: flex; + height: 40px; + padding: 8px 12px 8px 16px; + justify-content: center; + align-items: center; + gap: 6px; + align-self: stretch; + border: 0px; + border-radius: 50px; + margin-top: 24px; + cursor: pointer; + } + + .next-button.disabled { + opacity: 0.5; + cursor: not-allowed; + pointer-events: none; + } + + .arrow { + font-size: 18px; + color: #fff; + } + } + + .onboarding-questionaire-footer { + width: 100%; + height: 60px; + 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; + } +} diff --git a/frontend/src/container/OnboardingQuestionaire/OptimiseSignozNeeds/OptimiseSignozNeeds.tsx b/frontend/src/container/OnboardingQuestionaire/OptimiseSignozNeeds/OptimiseSignozNeeds.tsx new file mode 100644 index 0000000000..107b1830ef --- /dev/null +++ b/frontend/src/container/OnboardingQuestionaire/OptimiseSignozNeeds/OptimiseSignozNeeds.tsx @@ -0,0 +1,194 @@ +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 | null; + setOptimiseSignozDetails: (details: Record | null) => void; + onNext: () => void; + onBack: () => void; +} + +const logMarks: SliderSingleProps['marks'] = { + 0: '2 GB', + 25: '25 GB', + 50: '50 GB', + 100: '100 GB', +}; + +const hostMarks: SliderSingleProps['marks'] = { + 0: '0', + 20: '20', + 40: '40', + 60: '60', + 80: '80', + 100: '100', +}; + +const serviceMarks: SliderSingleProps['marks'] = { + 0: '0', + 20: '20', + 40: '40', + 60: '60', + 80: '80', + 100: '100', +}; + +function OptimiseSignozNeeds({ + optimiseSignozDetails, + setOptimiseSignozDetails, + onNext, + onBack, +}: OptimiseSignozNeedsProps): JSX.Element { + const [logsPerDay, setLogsPerDay] = useState( + optimiseSignozDetails?.logsPerDay || 25, + ); + const [hostsPerDay, setHostsPerDay] = useState( + optimiseSignozDetails?.hostsPerDay || 40, + ); + const [services, setServices] = useState( + 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 ( +
+ + Optimize SigNoz for Your Needs + + + Give us a quick sense of your scale so SigNoz can keep up! + + +
+
+ + What does your scale approximately look like? + + +
+ +
+ setLogsPerDay(value)} + styles={{ + track: { + background: '#4E74F8', + }, + }} + /> +
+
+ +
+ +
+ setHostsPerDay(value)} + styles={{ + track: { + background: '#4E74F8', + }, + }} + /> +
+
+ +
+ +
+ setServices(value)} + styles={{ + track: { + background: '#4E74F8', + }, + }} + /> +
+
+
+ +
+ + + +
+ +
+ +
+
+
+ ); +} + +export default OptimiseSignozNeeds; diff --git a/frontend/src/container/OnboardingQuestionaire/OrgQuestions/OrgQuestions.styles.scss b/frontend/src/container/OnboardingQuestionaire/OrgQuestions/OrgQuestions.styles.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/container/OnboardingQuestionaire/OrgQuestions/OrgQuestions.tsx b/frontend/src/container/OnboardingQuestionaire/OrgQuestions/OrgQuestions.tsx new file mode 100644 index 0000000000..6cd679c5da --- /dev/null +++ b/frontend/src/container/OnboardingQuestionaire/OrgQuestions/OrgQuestions.tsx @@ -0,0 +1,260 @@ +/* eslint-disable sonarjs/cognitive-complexity */ +import '../OnboardingQuestionaire.styles.scss'; + +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; +} + +const observabilityTools = [ + 'AWS Cloudwatch', + 'DataDog', + 'New Relic', + 'Grafana / Prometheus', + 'Azure App Monitor', + 'GCP-native o11y tools', + 'Honeycomb', +]; + +const o11yFamiliarityOptions: Record = { + 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( + orgDetails?.organisationName || '', + ); + const [usesObservability, setUsesObservability] = useState( + orgDetails?.usesObservability || null, + ); + const [observabilityTool, setObservabilityTool] = useState( + orgDetails?.observabilityTool || null, + ); + const [otherTool, setOtherTool] = useState( + orgDetails?.otherTool || '', + ); + const [familiarity, setFamiliarity] = useState( + orgDetails?.familiarity || null, + ); + const [isNextDisabled, setIsNextDisabled] = useState(true); + + const { user } = useSelector((state) => state.app); + + useEffect(() => { + if ( + organisationName !== '' && + usesObservability !== null && + familiarity !== null && + (observabilityTool !== 'Others' || (usesObservability && otherTool !== '')) + ) { + setIsNextDisabled(false); + } else { + setIsNextDisabled(true); + } + }, [ + organisationName, + usesObservability, + familiarity, + observabilityTool, + otherTool, + ]); + + const handleOnNext = (): void => { + setOrgDetails({ + organisationName, + usesObservability, + observabilityTool, + otherTool, + familiarity, + }); + + logEvent('Onboarding: Org Questions: Next', { + organisationName, + usesObservability, + observabilityTool, + otherTool, + familiarity, + }); + + onNext(); + }; + + return ( +
+ + Welcome, {user?.name}! + + + We'll help you get the most out of SigNoz, whether you're new to + observability or a seasoned pro. + + +
+
+
+ + setOrganisationName(e.target.value)} + /> +
+ +
+ + +
+ + +
+
+ + {usesObservability && ( +
+ +
+ {observabilityTools.map((tool) => ( + + ))} + + {observabilityTool === 'Others' ? ( + + ) : ( + '' + ) + } + onChange={(e): void => setOtherTool(e.target.value)} + /> + ) : ( + + )} +
+
+ )} + +
+
+ Are you familiar with observability (o11y)? +
+
+ {Object.keys(o11yFamiliarityOptions).map((option: string) => ( + + ))} +
+
+
+ +
+ +
+
+
+ ); +} + +export default OrgQuestions; diff --git a/frontend/src/container/OnboardingQuestionaire/index.tsx b/frontend/src/container/OnboardingQuestionaire/index.tsx new file mode 100644 index 0000000000..bb9e88d0d4 --- /dev/null +++ b/frontend/src/container/OnboardingQuestionaire/index.tsx @@ -0,0 +1,79 @@ +import './OnboardingQuestionaire.styles.scss'; + +import { useState } from 'react'; + +import { AboutSigNozQuestions } from './AboutSigNozQuestions/AboutSigNozQuestions'; +import InviteTeamMembers from './InviteTeamMembers/InviteTeamMembers'; +import { OnboardingFooter } from './OnboardingFooter/OnboardingFooter'; +import { OnboardingHeader } from './OnboardingHeader/OnboardingHeader'; +import OptimiseSignozNeeds from './OptimiseSignozNeeds/OptimiseSignozNeeds'; +import OrgQuestions from './OrgQuestions/OrgQuestions'; + +function OnboardingQuestionaire(): JSX.Element { + const [currentStep, setCurrentStep] = useState(1); + + const [orgDetails, setOrgDetails] = useState | null>( + null, + ); + const [signozDetails, setSignozDetails] = useState | null>(null); + const [optimiseSignozDetails, setOptimiseSignozDetails] = useState | null>(null); + + const [teamMembers, setTeamMembers] = useState(['']); + + return ( +
+
+ +
+ +
+ {currentStep === 1 && ( + setCurrentStep(2)} + /> + )} + + {currentStep === 2 && ( + setCurrentStep(1)} + onNext={(): void => setCurrentStep(3)} + /> + )} + + {currentStep === 3 && ( + setCurrentStep(2)} + onNext={(): void => setCurrentStep(4)} + /> + )} + + {currentStep === 4 && ( + setCurrentStep(3)} + onNext={(): void => setCurrentStep(5)} + /> + )} +
+ +
+ +
+
+ ); +} + +export default OnboardingQuestionaire; diff --git a/frontend/src/container/SideNav/config.ts b/frontend/src/container/SideNav/config.ts index 37e9db4d9f..7b37f64bb7 100644 --- a/frontend/src/container/SideNav/config.ts +++ b/frontend/src/container/SideNav/config.ts @@ -27,6 +27,7 @@ export const routeConfig: Record = { [ROUTES.ERROR_DETAIL]: [QueryParams.resourceAttributes], [ROUTES.HOME_PAGE]: [QueryParams.resourceAttributes], [ROUTES.GET_STARTED]: [QueryParams.resourceAttributes], + [ROUTES.GET_STARTED_V2]: [QueryParams.resourceAttributes], [ROUTES.LIST_ALL_ALERT]: [QueryParams.resourceAttributes], [ROUTES.LIST_LICENSES]: [QueryParams.resourceAttributes], [ROUTES.LOGIN]: [QueryParams.resourceAttributes], diff --git a/frontend/src/container/SideNav/menuItems.tsx b/frontend/src/container/SideNav/menuItems.tsx index be694227a1..2be4bd1443 100644 --- a/frontend/src/container/SideNav/menuItems.tsx +++ b/frontend/src/container/SideNav/menuItems.tsx @@ -30,6 +30,12 @@ export const getStartedMenuItem = { icon: , }; +export const getStartedV2MenuItem = { + key: ROUTES.GET_STARTED_V2, + label: 'Get Started V2', + icon: , +}; + export const inviteMemberMenuItem = { key: `${ROUTES.ORG_SETTINGS}#invite-team-members`, label: 'Invite Team Member', @@ -67,6 +73,11 @@ export const trySignozCloudMenuItem: SidebarItem = { }; const menuItems: SidebarItem[] = [ + { + key: ROUTES.GET_STARTED_V2, + label: 'Get Started V2', + icon: , + }, { key: ROUTES.APPLICATION, label: 'Services', diff --git a/frontend/src/container/TopNav/Breadcrumbs/index.tsx b/frontend/src/container/TopNav/Breadcrumbs/index.tsx index 9efd50d2c3..c98f4d05e2 100644 --- a/frontend/src/container/TopNav/Breadcrumbs/index.tsx +++ b/frontend/src/container/TopNav/Breadcrumbs/index.tsx @@ -9,6 +9,7 @@ const breadcrumbNameMap: Record = { [ROUTES.SERVICE_MAP]: 'Service Map', [ROUTES.USAGE_EXPLORER]: 'Usage Explorer', [ROUTES.GET_STARTED]: 'Get Started', + [ROUTES.GET_STARTED_V2]: 'Get Started V2', [ROUTES.ALL_CHANNELS]: 'Channels', [ROUTES.SETTINGS]: 'Settings', [ROUTES.DASHBOARD]: 'Dashboard', diff --git a/frontend/src/pages/OnboardingPageV2/OnboardingPageV2.tsx b/frontend/src/pages/OnboardingPageV2/OnboardingPageV2.tsx new file mode 100644 index 0000000000..7100894a41 --- /dev/null +++ b/frontend/src/pages/OnboardingPageV2/OnboardingPageV2.tsx @@ -0,0 +1,11 @@ +import OnboardingQuestionaire from 'container/OnboardingQuestionaire'; + +function OnboardingPageV2(): JSX.Element { + return ( +
+ +
+ ); +} + +export default OnboardingPageV2; diff --git a/frontend/src/pages/OnboardingPageV2/index.tsx b/frontend/src/pages/OnboardingPageV2/index.tsx new file mode 100644 index 0000000000..9fad953dfe --- /dev/null +++ b/frontend/src/pages/OnboardingPageV2/index.tsx @@ -0,0 +1,3 @@ +import OnboardingPage from './OnboardingPageV2'; + +export default OnboardingPage; diff --git a/frontend/src/utils/permission/index.ts b/frontend/src/utils/permission/index.ts index 8a35121f57..6052900a05 100644 --- a/frontend/src/utils/permission/index.ts +++ b/frontend/src/utils/permission/index.ts @@ -86,6 +86,7 @@ export const routePermission: Record = { LOGS_PIPELINES: ['ADMIN', 'EDITOR', 'VIEWER'], TRACE_EXPLORER: ['ADMIN', 'EDITOR', 'VIEWER'], GET_STARTED: ['ADMIN', 'EDITOR', 'VIEWER'], + GET_STARTED_V2: ['ADMIN', 'EDITOR', 'VIEWER'], GET_STARTED_APPLICATION_MONITORING: ['ADMIN', 'EDITOR', 'VIEWER'], GET_STARTED_INFRASTRUCTURE_MONITORING: ['ADMIN', 'EDITOR', 'VIEWER'], GET_STARTED_LOGS_MANAGEMENT: ['ADMIN', 'EDITOR', 'VIEWER'],