Skip to content

Commit 5bda338

Browse files
authored
Merge pull request #1698 from RedisInsight/fe/feature/RI-4067_onboard-new-users
#RI-4067 - onboard new users
2 parents 9743e53 + 1dcba56 commit 5bda338

File tree

66 files changed

+3115
-253
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+3115
-253
lines changed
Lines changed: 17 additions & 0 deletions
Loading

redisinsight/ui/src/components/analytics-tabs/AnalyticsTabs.tsx

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback } from 'react'
1+
import React, { useCallback, useEffect } from 'react'
22
import { EuiTab, EuiTabs } from '@elastic/eui'
33
import { useDispatch, useSelector } from 'react-redux'
44
import { useParams, useHistory } from 'react-router-dom'
@@ -9,17 +9,27 @@ import { analyticsSettingsSelector, setAnalyticsViewTab } from 'uiSrc/slices/ana
99
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
1010
import { ConnectionType } from 'uiSrc/slices/interfaces'
1111

12+
import { appFeatureOnboardingSelector, setOnboardNextStep } from 'uiSrc/slices/app/features'
13+
import { renderOnboardingTourWithChild } from 'uiSrc/utils/onboarding'
14+
import { OnboardingSteps } from 'uiSrc/constants/onboarding'
1215
import { analyticsViewTabs } from './constants'
1316

1417
const AnalyticsTabs = () => {
1518
const { viewTab } = useSelector(analyticsSettingsSelector)
1619
const { connectionType } = useSelector(connectedInstanceSelector)
20+
const { currentStep } = useSelector(appFeatureOnboardingSelector)
1721
const history = useHistory()
1822

1923
const { instanceId } = useParams<{ instanceId: string }>()
2024

2125
const dispatch = useDispatch()
2226

27+
useEffect(() => {
28+
if (connectionType !== ConnectionType.Cluster && currentStep === OnboardingSteps.AnalyticsOverview) {
29+
dispatch(setOnboardNextStep())
30+
}
31+
}, [])
32+
2333
const onSelectedTabChanged = (id: AnalyticsViewTab) => {
2434
if (id === AnalyticsViewTab.ClusterDetails) {
2535
history.push(Pages.clusterDetails(instanceId))
@@ -38,15 +48,20 @@ const AnalyticsTabs = () => {
3848
? [...analyticsViewTabs]
3949
: [...analyticsViewTabs].filter((tab) => tab.id !== AnalyticsViewTab.ClusterDetails)
4050

41-
return filteredAnalyticsViewTabs.map(({ id, label }) => (
42-
<EuiTab
43-
isSelected={viewTab === id}
44-
onClick={() => onSelectedTabChanged(id)}
45-
key={id}
46-
data-testid={`analytics-tab-${id}`}
47-
>
48-
{label}
49-
</EuiTab>
51+
return filteredAnalyticsViewTabs.map(({ id, label, onboard }) => renderOnboardingTourWithChild(
52+
(
53+
<EuiTab
54+
isSelected={viewTab === id}
55+
onClick={() => onSelectedTabChanged(id)}
56+
key={id}
57+
data-testid={`analytics-tab-${id}`}
58+
>
59+
{label}
60+
</EuiTab>
61+
),
62+
{ options: onboard, anchorPosition: 'downLeft' },
63+
viewTab === id,
64+
id
5065
))
5166
}, [viewTab, connectionType])
5267

redisinsight/ui/src/components/analytics-tabs/constants.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ import React, { ReactNode } from 'react'
22
import { useSelector } from 'react-redux'
33

44
import { AnalyticsViewTab } from 'uiSrc/slices/interfaces/analytics'
5-
import { appFeatureHighlightingSelector } from 'uiSrc/slices/app/features-highlighting'
5+
import { appFeatureHighlightingSelector } from 'uiSrc/slices/app/features'
66
import HighlightedFeature from 'uiSrc/components/hightlighted-feature/HighlightedFeature'
77
import { BUILD_FEATURES } from 'uiSrc/constants/featuresHighlighting'
88
import { getHighlightingFeatures } from 'uiSrc/utils/highlighting'
9+
import { OnboardingTourOptions } from 'uiSrc/components/onboarding-tour'
10+
import { ONBOARDING_FEATURES } from 'uiSrc/components/onboarding-features'
911

1012
interface AnalyticsTabs {
1113
id: AnalyticsViewTab
1214
label: string | ReactNode
15+
onboard?: OnboardingTourOptions
1316
}
1417

1518
const DatabaseAnalyticsTab = () => {
@@ -36,13 +39,16 @@ export const analyticsViewTabs: AnalyticsTabs[] = [
3639
{
3740
id: AnalyticsViewTab.ClusterDetails,
3841
label: 'Overview',
42+
onboard: ONBOARDING_FEATURES.ANALYTICS_OVERVIEW
3943
},
4044
{
4145
id: AnalyticsViewTab.DatabaseAnalysis,
4246
label: <DatabaseAnalyticsTab />,
47+
onboard: ONBOARDING_FEATURES.ANALYTICS_DATABASE_ANALYSIS
4348
},
4449
{
4550
id: AnalyticsViewTab.SlowLog,
4651
label: 'Slow Log',
52+
onboard: ONBOARDING_FEATURES.ANALYTICS_SLOW_LOG
4753
},
4854
]

redisinsight/ui/src/components/cli/components/cli-header/CliHeader.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
2323
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
2424
import { outputSelector, resetOutputLoading } from 'uiSrc/slices/cli/cli-output'
2525
import { getDbIndex } from 'uiSrc/utils'
26+
import { OnboardingTour } from 'uiSrc/components'
27+
import { ONBOARDING_FEATURES } from 'uiSrc/components/onboarding-features'
2628

2729
import styles from './styles.module.scss'
2830

@@ -81,7 +83,13 @@ const CliHeader = () => {
8183
>
8284
<EuiFlexItem grow={false} className={styles.title}>
8385
<EuiIcon type="console" size="m" />
84-
<EuiText>CLI</EuiText>
86+
<OnboardingTour
87+
options={ONBOARDING_FEATURES.BROWSER_CLI}
88+
anchorPosition="upLeft"
89+
panelClassName={styles.cliOnboardPanel}
90+
>
91+
<EuiText>CLI</EuiText>
92+
</OnboardingTour>
8593
</EuiFlexItem>
8694
<EuiFlexItem grow />
8795
<EuiFlexItem grow={false}>

redisinsight/ui/src/components/cli/components/cli-header/styles.module.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,7 @@
4343
}
4444
}
4545
}
46+
47+
.cliOnboardPanel {
48+
margin-top: -4px;
49+
}

redisinsight/ui/src/components/command-helper/CommandHelperHeader/CommandHelperHeader.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
} from '@elastic/eui'
1313
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
1414
import { resetCliHelperSettings, toggleCliHelper, toggleHideCliHelper } from 'uiSrc/slices/cli/cli-settings'
15+
import { OnboardingTour } from 'uiSrc/components'
16+
import { ONBOARDING_FEATURES } from 'uiSrc/components/onboarding-features'
1517

1618
import styles from './styles.module.scss'
1719

@@ -51,7 +53,13 @@ const CommandHelperHeader = () => {
5153
>
5254
<EuiFlexItem grow={false} className={styles.title}>
5355
<EuiIcon type="documents" size="m" />
54-
<EuiText style={{ lineHeight: '1rem' }}>Command Helper</EuiText>
56+
<OnboardingTour
57+
options={ONBOARDING_FEATURES.BROWSER_COMMAND_HELPER}
58+
anchorPosition="upLeft"
59+
panelClassName={styles.helperOnboardPanel}
60+
>
61+
<EuiText>Command Helper</EuiText>
62+
</OnboardingTour>
5563
</EuiFlexItem>
5664
<EuiFlexItem grow />
5765
<EuiFlexItem grow={false}>

redisinsight/ui/src/components/command-helper/CommandHelperHeader/styles.module.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,7 @@
2424
}
2525
}
2626
}
27+
28+
.helperOnboardPanel {
29+
margin-top: -4px;
30+
}

redisinsight/ui/src/components/config/Config.spec.tsx

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react'
22
import { cloneDeep } from 'lodash'
33
import { BuildType } from 'uiSrc/constants/env'
44
import { localStorageService } from 'uiSrc/services'
5-
import { setFeaturesToHighlight } from 'uiSrc/slices/app/features-highlighting'
5+
import { setFeaturesToHighlight, setOnboarding } from 'uiSrc/slices/app/features'
66
import { getNotifications } from 'uiSrc/slices/app/notifications'
77
import { render, mockedStore, cleanup, MOCKED_HIGHLIGHTING_FEATURES } from 'uiSrc/utils/test-utils'
88

@@ -14,6 +14,7 @@ import {
1414
import { appServerInfoSelector, getServerInfo } from 'uiSrc/slices/app/info'
1515
import { processCliClient } from 'uiSrc/slices/cli/cli-settings'
1616
import { getRedisCommands } from 'uiSrc/slices/app/redis-commands'
17+
import { ONBOARDING_FEATURES } from 'uiSrc/components/onboarding-features'
1718
import Config from './Config'
1819

1920
let store: typeof mockedStore
@@ -48,6 +49,8 @@ jest.mock('uiSrc/services', () => ({
4849
},
4950
}))
5051

52+
const onboardingTotalSteps = Object.keys(ONBOARDING_FEATURES).length
53+
5154
describe('Config', () => {
5255
it('should render', () => {
5356
render(<Config />)
@@ -169,4 +172,43 @@ describe('Config', () => {
169172
expect(store.getActions())
170173
.toEqual(expect.arrayContaining([setFeaturesToHighlight({ version: '2.0.12', features: MOCKED_HIGHLIGHTING_FEATURES })]))
171174
})
175+
176+
it('should call setOnboarding for new user', () => {
177+
const userSettingsSelectorMock = jest.fn().mockReturnValue({
178+
config: {
179+
agreements: null,
180+
}
181+
})
182+
const appServerInfoSelectorMock = jest.fn().mockReturnValue({
183+
buildType: BuildType.Electron,
184+
})
185+
userSettingsSelector.mockImplementation(userSettingsSelectorMock)
186+
appServerInfoSelector.mockImplementation(appServerInfoSelectorMock)
187+
188+
render(<Config />)
189+
190+
expect(store.getActions()).toEqual(expect.arrayContaining([setOnboarding(
191+
{ currentStep: 0, totalSteps: onboardingTotalSteps }
192+
)]))
193+
})
194+
195+
it('should call setOnboarding for existing user with not completed process', () => {
196+
localStorageService.get = jest.fn().mockReturnValue(5)
197+
const userSettingsSelectorMock = jest.fn().mockReturnValue({
198+
config: {
199+
agreements: {},
200+
}
201+
})
202+
const appServerInfoSelectorMock = jest.fn().mockReturnValue({
203+
buildType: BuildType.Electron,
204+
})
205+
userSettingsSelector.mockImplementation(userSettingsSelectorMock)
206+
appServerInfoSelector.mockImplementation(appServerInfoSelectorMock)
207+
208+
render(<Config />)
209+
210+
expect(store.getActions()).toEqual(expect.arrayContaining([setOnboarding(
211+
{ currentStep: 5, totalSteps: onboardingTotalSteps }
212+
)]))
213+
})
172214
})

redisinsight/ui/src/components/config/Config.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { useEffect } from 'react'
22
import { useDispatch, useSelector } from 'react-redux'
33
import { useLocation } from 'react-router-dom'
4+
import { isNumber } from 'lodash'
45
import { BrowserStorageItem } from 'uiSrc/constants'
56
import { BuildType } from 'uiSrc/constants/env'
67
import { BUILD_FEATURES } from 'uiSrc/constants/featuresHighlighting'
78
import { localStorageService } from 'uiSrc/services'
8-
import { setFeaturesToHighlight } from 'uiSrc/slices/app/features-highlighting'
9+
import { setFeaturesToHighlight, setOnboarding } from 'uiSrc/slices/app/features'
910
import { fetchNotificationsAction } from 'uiSrc/slices/app/notifications'
1011

1112
import {
@@ -26,6 +27,7 @@ import { checkIsAnalyticsGranted } from 'uiSrc/telemetry/checkAnalytics'
2627
import { setFavicon, isDifferentConsentsExists } from 'uiSrc/utils'
2728
import { fetchUnsupportedCliCommandsAction } from 'uiSrc/slices/cli/cli-settings'
2829
import { fetchRedisCommandsInfo } from 'uiSrc/slices/app/redis-commands'
30+
import { ONBOARDING_FEATURES } from 'uiSrc/components/onboarding-features'
2931
import favicon from 'uiSrc/assets/favicon.ico'
3032

3133
const SETTINGS_PAGE_PATH = '/settings'
@@ -68,6 +70,7 @@ const Config = () => {
6870
}
6971

7072
featuresHighlight()
73+
onboardUsers()
7174
}, [serverInfo, config])
7275

7376
const featuresHighlight = () => {
@@ -96,6 +99,20 @@ const Config = () => {
9699
localStorageService.set(BrowserStorageItem.featuresHighlighting, data)
97100
}
98101

102+
const onboardUsers = () => {
103+
if (serverInfo?.buildType === BuildType.Electron && config) {
104+
const totalSteps = Object.keys(ONBOARDING_FEATURES).length
105+
const userCurrentStep = localStorageService.get(BrowserStorageItem.onboardingStep)
106+
107+
if (!config.agreements || isNumber(userCurrentStep)) {
108+
dispatch(setOnboarding({
109+
currentStep: config.agreements ? userCurrentStep : 0,
110+
totalSteps
111+
}))
112+
}
113+
}
114+
}
115+
99116
const checkSettingsToShowPopup = () => {
100117
const specConsents = spec?.agreements
101118
const appliedConsents = config?.agreements

redisinsight/ui/src/components/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import MonitorWrapper from './monitor'
2020
import PagePlaceholder from './page-placeholder'
2121
import BulkActionsConfig from './bulk-actions-config'
2222
import ImportDatabasesDialog from './import-databases-dialog'
23+
import OnboardingTour from './onboarding-tour'
2324

2425
export {
2526
NavigationMenu,
@@ -46,5 +47,6 @@ export {
4647
ShortcutsFlyout,
4748
PagePlaceholder,
4849
BulkActionsConfig,
49-
ImportDatabasesDialog
50+
ImportDatabasesDialog,
51+
OnboardingTour
5052
}

0 commit comments

Comments
 (0)