Skip to content

Commit

Permalink
feat: add flag and onboarding steps for dashboard templates (#23069)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Zach Waterfield <[email protected]>
  • Loading branch information
3 people authored Aug 29, 2024
1 parent 261a2f5 commit 719950b
Show file tree
Hide file tree
Showing 17 changed files with 269 additions and 33 deletions.
Binary file modified frontend/__snapshots__/scenes-app-dashboards--new--dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/scenes-app-dashboards--new--light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ export const FEATURE_FLAGS = {
ALERTS: 'alerts', // owner: github.com/nikitaevg
ERROR_TRACKING: 'error-tracking', // owner: #team-replay
SETTINGS_BOUNCE_RATE_PAGE_VIEW_MODE: 'settings-bounce-rate-page-view-mode', // owner: @robbie-c
ONBOARDING_DASHBOARD_TEMPLATES: 'onboarding-dashboard-templates', // owner: @raquelmsmith
MULTIPLE_BREAKDOWNS: 'multiple-breakdowns', // owner: @skoob13 #team-product-analytics
WEB_ANALYTICS_LIVE_USER_COUNT: 'web-analytics-live-user-count', // owner: @robbie-c
SETTINGS_SESSION_TABLE_VERSION: 'settings-session-table-version', // owner: @robbie-c
Expand Down
23 changes: 20 additions & 3 deletions frontend/src/scenes/dashboard/DashboardTemplateChooser.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import './DashboardTemplateChooser.scss'

import { LemonTag } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { useActions, useValues } from 'kea'
import { FallbackCoverImage } from 'lib/components/FallbackCoverImage/FallbackCoverImage'
Expand All @@ -14,7 +15,11 @@ import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'

import { DashboardTemplateType } from '~/types'

export function DashboardTemplateChooser({ scope = 'default' }: DashboardTemplateProps): JSX.Element {
export function DashboardTemplateChooser({
scope = 'default',
onItemClick,
redirectAfterCreation = true,
}: DashboardTemplateProps): JSX.Element {
const templatesLogic = dashboardTemplatesLogic({ scope })
const { allTemplates, allTemplatesLoading } = useValues(templatesLogic)

Expand Down Expand Up @@ -67,14 +72,19 @@ export function DashboardTemplateChooser({ scope = 'default' }: DashboardTemplat
if (template.variables === null) {
template.variables = []
}
createDashboardFromTemplate(template, template.variables || [])
createDashboardFromTemplate(
template,
template.variables || [],
redirectAfterCreation
)
} else {
if (!newDashboardModalVisible) {
showVariableSelectModal(template)
} else {
setActiveDashboardTemplate(template)
}
}
onItemClick?.()
}}
index={index + 1}
data-attr="create-dashboard-from-template"
Expand All @@ -92,7 +102,7 @@ function TemplateItem({
index,
'data-attr': dataAttr,
}: {
template: Pick<DashboardTemplateType, 'template_name' | 'dashboard_description' | 'image_url'>
template: Pick<DashboardTemplateType, 'template_name' | 'dashboard_description' | 'image_url' | 'tags'>
onClick: () => void
index: number
'data-attr': string
Expand All @@ -114,6 +124,13 @@ function TemplateItem({
</div>

<h5 className="px-2 mb-1">{template?.template_name}</h5>
<div className="flex gap-x-1 px-2 mb-1">
{template.tags?.map((tag, index) => (
<LemonTag key={index} type="option">
{tag}
</LemonTag>
))}
</div>
<div className="px-2 py-1 overflow-y-auto grow">
<p className={clsx('text-muted-alt text-xs', isHovering ? '' : 'line-clamp-2')}>
{template?.dashboard_description ?? ' '}
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/scenes/dashboard/DashboardTemplateVariables.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LemonLabel } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { useEffect } from 'react'
import { ActionFilter } from 'scenes/insights/filters/ActionFilter/ActionFilter'

import { FilterType, InsightType } from '~/types'
Expand All @@ -9,12 +10,16 @@ import { newDashboardLogic } from './newDashboardLogic'

export function DashboardTemplateVariables(): JSX.Element {
const { activeDashboardTemplate } = useValues(newDashboardLogic)

const theDashboardTemplateVariablesLogic = dashboardTemplateVariablesLogic({
variables: activeDashboardTemplate?.variables || [],
})
const { variables } = useValues(theDashboardTemplateVariablesLogic)
const { setVariable } = useActions(theDashboardTemplateVariablesLogic)
const { setVariable, setVariables } = useActions(theDashboardTemplateVariablesLogic)

// this is a hack, I'm not sure why it's not set properly initially. Figure it out.
useEffect(() => {
setVariables(activeDashboardTemplate?.variables || [])
}, [activeDashboardTemplate])

return (
<div className="mb-4 DashboardTemplateVariables max-w-192">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import type { dashboardTemplatesLogicType } from './dashboardTemplatesLogicType'
export interface DashboardTemplateProps {
// default is to present global templates _and_ those visible only in the current team
scope?: 'default' | DashboardTemplateScope
onItemClick?: () => void
redirectAfterCreation?: boolean
}

export const dashboardTemplatesLogic = kea<dashboardTemplatesLogicType>([
Expand Down
27 changes: 20 additions & 7 deletions frontend/src/scenes/dashboard/newDashboardLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,19 @@ export const newDashboardLogic = kea<newDashboardLogicType>([
addDashboard: (form: Partial<NewDashboardForm>) => ({ form }),
setActiveDashboardTemplate: (template: DashboardTemplateType) => ({ template }),
clearActiveDashboardTemplate: true,
createDashboardFromTemplate: (template: DashboardTemplateType, variables: DashboardTemplateVariableType[]) => ({
createDashboardFromTemplate: (
template: DashboardTemplateType,
variables: DashboardTemplateVariableType[],
redirectAfterCreation?: boolean
) => ({
template,
variables,
redirectAfterCreation,
}),
submitNewDashboardSuccessWithResult: (result: DashboardType, variables?: DashboardTemplateVariableType[]) => ({
result,
variables,
}),
submitNewDashboardSuccessWithResult: (result: DashboardType) => ({ result }),
}),
reducers({
isLoading: [
Expand Down Expand Up @@ -137,7 +145,8 @@ export const newDashboardLogic = kea<newDashboardLogicType>([
)
actions.hideNewDashboardModal()
actions.resetNewDashboard()
dashboardsModel.actions.addDashboardSuccess(getQueryBasedDashboard(result)!)
const queryBasedDashboard = getQueryBasedDashboard(result)
queryBasedDashboard && dashboardsModel.actions.addDashboardSuccess(queryBasedDashboard)
actions.submitNewDashboardSuccessWithResult(result)
if (show) {
breakpoint()
Expand Down Expand Up @@ -169,7 +178,8 @@ export const newDashboardLogic = kea<newDashboardLogicType>([
actions.clearActiveDashboardTemplate()
actions.resetNewDashboard()
},
createDashboardFromTemplate: async ({ template, variables }) => {
createDashboardFromTemplate: async ({ template, variables, redirectAfterCreation = true }) => {
actions.setIsLoading(true)
const tiles = makeTilesUsingVariables(template.tiles, variables)
const dashboardJSON = {
...template,
Expand All @@ -183,9 +193,12 @@ export const newDashboardLogic = kea<newDashboardLogicType>([
)
actions.hideNewDashboardModal()
actions.resetNewDashboard()
dashboardsModel.actions.addDashboardSuccess(getQueryBasedDashboard(result)!)
actions.submitNewDashboardSuccessWithResult(result)
router.actions.push(urls.dashboard(result.id))
const queryBasedDashboard = getQueryBasedDashboard(result)
queryBasedDashboard && dashboardsModel.actions.addDashboardSuccess(queryBasedDashboard)
actions.submitNewDashboardSuccessWithResult(result, variables)
if (redirectAfterCreation) {
router.actions.push(urls.dashboard(result.id))
}
} catch (e: any) {
if (!isBreakpoint(e)) {
const message = e.code && e.detail ? `${e.code}: ${e.detail}` : e
Expand Down
15 changes: 14 additions & 1 deletion frontend/src/scenes/onboarding/Onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { FEATURE_FLAGS, SESSION_REPLAY_MINIMUM_DURATION_OPTIONS } from 'lib/cons
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { useEffect, useState } from 'react'
import { billingLogic } from 'scenes/billing/billingLogic'
import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'
import { AndroidInstructions } from 'scenes/onboarding/sdks/session-replay'
import { SceneExport } from 'scenes/sceneTypes'
import { teamLogic } from 'scenes/teamLogic'
Expand All @@ -19,6 +20,8 @@ import { OnboardingProductConfiguration } from './OnboardingProductConfiguration
import { ProductConfigOption } from './onboardingProductConfigurationLogic'
import { OnboardingProductIntroduction } from './OnboardingProductIntroduction'
import { OnboardingReverseProxy } from './OnboardingReverseProxy'
import { OnboardingDashboardTemplateConfigureStep } from './productAnalyticsSteps/DashboardTemplateConfigureStep'
import { OnboardingDashboardTemplateSelectStep } from './productAnalyticsSteps/DashboardTemplateSelectStep'
import { FeatureFlagsSDKInstructions } from './sdks/feature-flags/FeatureFlagsSDKInstructions'
import { ProductAnalyticsSDKInstructions } from './sdks/product-analytics/ProductAnalyticsSDKInstructions'
import { SDKs } from './sdks/SDKs'
Expand Down Expand Up @@ -84,7 +87,7 @@ const OnboardingWrapper = ({ children }: { children: React.ReactNode }): JSX.Ele
steps = [...steps, BillingStep]
}
const inviteTeammatesStep = <OnboardingInviteTeammates stepKey={OnboardingStepKey.INVITE_TEAMMATES} />
steps = [...steps, inviteTeammatesStep]
steps = [...steps, inviteTeammatesStep].filter(Boolean)
setAllSteps(steps)
}

Expand All @@ -101,6 +104,10 @@ const OnboardingWrapper = ({ children }: { children: React.ReactNode }): JSX.Ele

const ProductAnalyticsOnboarding = (): JSX.Element => {
const { currentTeam } = useValues(teamLogic)
const { featureFlags } = useValues(featureFlagLogic)
// mount the logic here so that it stays mounted for the entire onboarding flow
// not sure if there is a better way to do this
useValues(newDashboardLogic)

const options: ProductConfigOption[] = [
{
Expand Down Expand Up @@ -158,6 +165,12 @@ const ProductAnalyticsOnboarding = (): JSX.Element => {
stepKey={OnboardingStepKey.INSTALL}
/>
<OnboardingProductConfiguration stepKey={OnboardingStepKey.PRODUCT_CONFIGURATION} options={options} />
{featureFlags[FEATURE_FLAGS.ONBOARDING_DASHBOARD_TEMPLATES] == 'test' ? (
<OnboardingDashboardTemplateSelectStep stepKey={OnboardingStepKey.DASHBOARD_TEMPLATE} />
) : null}
{featureFlags[FEATURE_FLAGS.ONBOARDING_DASHBOARD_TEMPLATES] == 'test' ? (
<OnboardingDashboardTemplateConfigureStep stepKey={OnboardingStepKey.DASHBOARD_TEMPLATE_CONFIGURE} />
) : null}
</OnboardingWrapper>
)
}
Expand Down
44 changes: 31 additions & 13 deletions frontend/src/scenes/onboarding/OnboardingStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { useActions, useValues } from 'kea'
import { supportLogic } from 'lib/components/Support/supportLogic'
import { IconChevronRight } from 'lib/lemon-ui/icons'
import React from 'react'
import { urls } from 'scenes/urls'

import { onboardingLogic, OnboardingStepKey, stepKeyToTitle } from './onboardingLogic'
import { breadcrumbExcludeSteps, onboardingLogic, OnboardingStepKey, stepKeyToTitle } from './onboardingLogic'
import { onboardingTemplateConfigLogic } from './productAnalyticsSteps/onboardingTemplateConfigLogic'

export const OnboardingStep = ({
stepKey,
Expand All @@ -19,6 +21,7 @@ export const OnboardingStep = ({
continueText,
continueOverride,
hideHeader,
breadcrumbHighlightName,
}: {
stepKey: OnboardingStepKey
title: string
Expand All @@ -31,14 +34,17 @@ export const OnboardingStep = ({
continueText?: string
continueOverride?: JSX.Element
hideHeader?: boolean
breadcrumbHighlightName?: OnboardingStepKey
}): JSX.Element => {
const { hasNextStep, onboardingStepKeys, currentOnboardingStep } = useValues(onboardingLogic)
const { completeOnboarding, goToNextStep, setStepKey } = useActions(onboardingLogic)
const { openSupportForm } = useActions(supportLogic)
const { dashboardCreatedDuringOnboarding } = useValues(onboardingTemplateConfigLogic)

if (!stepKey) {
throw new Error('stepKey is required in any OnboardingStep')
}
const breadcrumbStepKeys = onboardingStepKeys.filter((stepKey) => !breadcrumbExcludeSteps.includes(stepKey))

return (
<>
Expand All @@ -48,26 +54,24 @@ export const OnboardingStep = ({
className="flex items-center justify-start gap-x-3 px-2 shrink-0 w-full"
data-attr="onboarding-breadcrumbs"
>
{onboardingStepKeys.map((stepName, idx) => {
{breadcrumbStepKeys.map((stepName, idx) => {
const highlightStep = [
currentOnboardingStep?.props.stepKey,
breadcrumbHighlightName,
].includes(stepName)
return (
<React.Fragment key={`stepKey-${idx}`}>
<Link
className={`text-sm ${
currentOnboardingStep?.props.stepKey === stepName && 'font-bold'
} font-bold`}
className={`text-sm ${highlightStep && 'font-bold'} font-bold`}
data-text={stepKeyToTitle(stepName)}
key={stepName}
onClick={() => setStepKey(stepName)}
>
<span
className={`text-sm ${
currentOnboardingStep?.props.stepKey !== stepName && 'text-muted'
}`}
>
<span className={`text-sm ${!highlightStep && 'text-muted'}`}>
{stepKeyToTitle(stepName)}
</span>
</Link>
{onboardingStepKeys.length > 1 && idx !== onboardingStepKeys.length - 1 && (
{breadcrumbStepKeys.length > 1 && idx !== breadcrumbStepKeys.length - 1 && (
<IconChevronRight className="text-xl" />
)}
</React.Fragment>
Expand Down Expand Up @@ -96,7 +100,14 @@ export const OnboardingStep = ({
type="secondary"
onClick={() => {
onSkip && onSkip()
!hasNextStep ? completeOnboarding() : goToNextStep()
!hasNextStep
? completeOnboarding(
undefined,
dashboardCreatedDuringOnboarding
? urls.dashboard(dashboardCreatedDuringOnboarding.id)
: undefined
)
: goToNextStep()
}}
data-attr="onboarding-skip-button"
>
Expand All @@ -112,7 +123,14 @@ export const OnboardingStep = ({
data-attr="onboarding-continue"
onClick={() => {
continueAction && continueAction()
!hasNextStep ? completeOnboarding() : goToNextStep()
!hasNextStep
? completeOnboarding(
undefined,
dashboardCreatedDuringOnboarding
? urls.dashboard(dashboardCreatedDuringOnboarding.id)
: undefined
)
: goToNextStep()
}}
sideIcon={hasNextStep ? <IconArrowRight /> : null}
>
Expand Down
Loading

0 comments on commit 719950b

Please sign in to comment.