diff --git a/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx b/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx
index 32b569226dcaa..ed3bd39277311 100644
--- a/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx
+++ b/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx
@@ -96,6 +96,7 @@ interface LemonCollapsePanelProps {
onChange: (isExpanded: boolean) => void
className?: string
dataAttr?: string
+ onHeaderClick?: () => void
}
function LemonCollapsePanel({
@@ -106,13 +107,17 @@ function LemonCollapsePanel({
className,
dataAttr,
onChange,
+ onHeaderClick,
}: LemonCollapsePanelProps): JSX.Element {
const { height: contentHeight, ref: contentRef } = useResizeObserver({ box: 'border-box' })
return (
onChange(!isExpanded)}
+ onClick={() => {
+ onHeaderClick && onHeaderClick()
+ onChange(!isExpanded)
+ }}
icon={isExpanded ? : }
className="LemonCollapsePanel__header"
{...(dataAttr ? { 'data-attr': dataAttr } : {})}
diff --git a/frontend/src/scenes/dashboard/dashboardTemplateVariablesLogic.ts b/frontend/src/scenes/dashboard/dashboardTemplateVariablesLogic.ts
index 39b45617e66ac..da23d22466d25 100644
--- a/frontend/src/scenes/dashboard/dashboardTemplateVariablesLogic.ts
+++ b/frontend/src/scenes/dashboard/dashboardTemplateVariablesLogic.ts
@@ -1,4 +1,4 @@
-import { actions, kea, path, props, propsChanged, reducers } from 'kea'
+import { actions, kea, listeners, path, props, propsChanged, reducers, selectors } from 'kea'
import { isEmptyObject } from 'lib/utils'
import { DashboardTemplateVariableType, FilterType, Optional } from '~/types'
@@ -24,6 +24,11 @@ export const dashboardTemplateVariablesLogic = kea ({ index }),
+ incrementActiveVariableIndex: true,
+ possiblyIncrementActiveVariableIndex: true,
+ resetVariable: (variableId: string) => ({ variableId }),
+ goToNextUntouchedActiveVariableIndex: true,
}),
reducers({
variables: [
@@ -41,14 +46,64 @@ export const dashboardTemplateVariablesLogic = kea {
if (v.name === variableName && filterGroup?.events?.length && filterGroup.events[0]) {
- return { ...v, default: filterGroup.events[0] }
+ return { ...v, default: filterGroup.events[0], touched: true }
}
- return v
+ return { ...v }
+ })
+ },
+ resetVariable: (state, { variableId }) => {
+ return state.map((v: DashboardTemplateVariableType) => {
+ if (v.id === variableId) {
+ return { ...v, default: FALLBACK_EVENT, touched: false }
+ }
+ return { ...v }
})
},
},
],
+ activeVariableIndex: [
+ 0,
+ {
+ setActiveVariableIndex: (_, { index }) => index,
+ incrementActiveVariableIndex: (state) => state + 1,
+ },
+ ],
}),
+ selectors(() => ({
+ activeVariable: [
+ (s) => [s.variables, s.activeVariableIndex],
+ (variables: DashboardTemplateVariableType[], activeVariableIndex: number) => {
+ return variables[activeVariableIndex]
+ },
+ ],
+ allVariablesAreTouched: [
+ (s) => [s.variables],
+ (variables: DashboardTemplateVariableType[]) => {
+ return variables.every((v) => v.touched)
+ },
+ ],
+ })),
+ listeners(({ actions, props, values }) => ({
+ possiblyIncrementActiveVariableIndex: () => {
+ if (props.variables.length > 0 && values.activeVariableIndex < props.variables.length - 1) {
+ actions.incrementActiveVariableIndex()
+ }
+ },
+ goToNextUntouchedActiveVariableIndex: () => {
+ let nextIndex = values.variables.findIndex((v, i) => !v.touched && i > values.activeVariableIndex)
+ if (nextIndex !== -1) {
+ actions.setActiveVariableIndex(nextIndex)
+ return
+ }
+ if (nextIndex == -1) {
+ nextIndex = values.variables.findIndex((v) => !v.touched)
+ if (nextIndex == -1) {
+ nextIndex = values.activeVariableIndex
+ }
+ }
+ actions.setActiveVariableIndex(nextIndex)
+ },
+ })),
propsChanged(({ actions, props }, oldProps) => {
if (props.variables !== oldProps.variables) {
actions.setVariables(props.variables)
diff --git a/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateConfigureStep.tsx b/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateConfigureStep.tsx
index 1f67ee2abd399..2bed8e8a07d2f 100644
--- a/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateConfigureStep.tsx
+++ b/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateConfigureStep.tsx
@@ -1,17 +1,17 @@
import { IconArrowRight } from '@posthog/icons'
-import { LemonButton, LemonCard, LemonSkeleton } from '@posthog/lemon-ui'
+import { LemonButton, LemonCard, LemonSkeleton, Link } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { authorizedUrlListLogic, AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic'
import { IframedToolbarBrowser } from 'lib/components/IframedToolbarBrowser/IframedToolbarBrowser'
import { iframedToolbarBrowserLogic } from 'lib/components/IframedToolbarBrowser/iframedToolbarBrowserLogic'
import { useRef, useState } from 'react'
-import { DashboardTemplateVariables } from 'scenes/dashboard/DashboardTemplateVariables'
import { dashboardTemplateVariablesLogic } from 'scenes/dashboard/dashboardTemplateVariablesLogic'
import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'
import { OnboardingStepKey } from '../onboardingLogic'
import { OnboardingStep } from '../OnboardingStep'
import { sdksLogic } from '../sdks/sdksLogic'
+import { DashboardTemplateVariables } from './DashboardTemplateVariables'
import { onboardingTemplateConfigLogic } from './onboardingTemplateConfigLogic'
export const OnboardingDashboardTemplateConfigureStep = ({
@@ -23,11 +23,14 @@ export const OnboardingDashboardTemplateConfigureStep = ({
const { activeDashboardTemplate } = useValues(onboardingTemplateConfigLogic)
const { createDashboardFromTemplate } = useActions(newDashboardLogic)
const { isLoading } = useValues(newDashboardLogic)
- const { variables } = useValues(dashboardTemplateVariablesLogic)
const { snippetHosts } = useValues(sdksLogic)
const { addUrl } = useActions(authorizedUrlListLogic({ actionId: null, type: AuthorizedUrlListType.TOOLBAR_URLS }))
const { setBrowserUrl } = useActions(iframedToolbarBrowserLogic({ iframeRef, clearBrowserUrlOnUnmount: true }))
const { browserUrl } = useValues(iframedToolbarBrowserLogic({ iframeRef, clearBrowserUrlOnUnmount: true }))
+ const theDashboardTemplateVariablesLogic = dashboardTemplateVariablesLogic({
+ variables: activeDashboardTemplate?.variables || [],
+ })
+ const { variables, allVariablesAreTouched } = useValues(theDashboardTemplateVariablesLogic)
const [isSubmitting, setIsSubmitting] = useState(false)
@@ -105,7 +108,15 @@ export const OnboardingDashboardTemplateConfigureStep = ({
)}
-
+
+ For each action below, select an element on your site that indicates when that action is
+ taken, or enter a custom event name that you'll send using{' '}
+
+ posthog.capture()
+ {' '}
+ (no need to send it now) .
+
+
Create dashboard
diff --git a/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateVariables.tsx b/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateVariables.tsx
new file mode 100644
index 0000000000000..e22f822eedcfe
--- /dev/null
+++ b/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateVariables.tsx
@@ -0,0 +1,186 @@
+import { IconCheckCircle, IconInfo, IconTrash } from '@posthog/icons'
+import { LemonBanner, LemonButton, LemonCollapse, LemonInput, LemonLabel } from '@posthog/lemon-ui'
+import { useActions, useValues } from 'kea'
+import { useEffect, useState } from 'react'
+import { dashboardTemplateVariablesLogic } from 'scenes/dashboard/dashboardTemplateVariablesLogic'
+import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'
+
+import { DashboardTemplateVariableType } from '~/types'
+
+function VariableSelector({
+ variable,
+ hasSelectedSite,
+}: {
+ variable: DashboardTemplateVariableType
+ hasSelectedSite: boolean
+}): JSX.Element {
+ const { activeDashboardTemplate } = useValues(newDashboardLogic)
+ const theDashboardTemplateVariablesLogic = dashboardTemplateVariablesLogic({
+ variables: activeDashboardTemplate?.variables || [],
+ })
+ const { setVariable, resetVariable, goToNextUntouchedActiveVariableIndex, incrementActiveVariableIndex } =
+ useActions(theDashboardTemplateVariablesLogic)
+ const { allVariablesAreTouched, variables, activeVariableIndex } = useValues(theDashboardTemplateVariablesLogic)
+ const [customEventName, setCustomEventName] = useState
(null)
+ const [showCustomEventField, setShowCustomEventField] = useState(false)
+
+ const FALLBACK_EVENT = {
+ id: '$other_event',
+ math: 'dau',
+ type: 'events',
+ }
+
+ return (
+
+
+
+ {variable.description}
+
+
+ {variable.touched && !customEventName && (
+
+
+
{' '}
+
Selected
+
.md-invite-button
+
+
+ }
+ type="tertiary"
+ size="small"
+ onClick={() => resetVariable(variable.id)}
+ />
+
+
+ )}
+ {showCustomEventField && (
+
+
Custom event name
+
+ Set the name that you'll use for a custom event (eg. a backend event) instead of selecting an
+ event from your site. You can change this later if needed.
+
+
+
{
+ if (v) {
+ setCustomEventName(v)
+ setVariable(variable.name, {
+ events: [{ id: v, math: 'dau', type: 'events' }],
+ })
+ } else {
+ setCustomEventName(null)
+ resetVariable(variable.id)
+ }
+ }}
+ onBlur={() => {
+ if (customEventName) {
+ setVariable(variable.name, {
+ events: [{ id: customEventName, math: 'dau', type: 'events' }],
+ })
+ } else {
+ resetVariable(variable.id)
+ setShowCustomEventField(false)
+ }
+ }}
+ />
+
+ }
+ type="tertiary"
+ size="small"
+ onClick={() => {
+ resetVariable(variable.id)
+ setCustomEventName(null)
+ setShowCustomEventField(false)
+ }}
+ />
+
+
+
+ )}
+ {!hasSelectedSite ? (
+
Please select a site to continue.
+ ) : (
+
+ {variable.touched ? (
+ <>
+ {!allVariablesAreTouched ||
+ (allVariablesAreTouched && variables.length !== activeVariableIndex + 1) ? (
+
+ !allVariablesAreTouched
+ ? goToNextUntouchedActiveVariableIndex()
+ : variables.length !== activeVariableIndex + 1
+ ? incrementActiveVariableIndex()
+ : null
+ }
+ >
+ Continue
+
+ ) : null}
+ >
+ ) : (
+
+ {
+ setShowCustomEventField(false)
+ setVariable(variable.name, { events: [FALLBACK_EVENT] })
+ }}
+ >
+ Select from site
+
+ setShowCustomEventField(true)}>
+ Use custom event
+
+
+ )}
+
+ )}
+
+ )
+}
+
+export function DashboardTemplateVariables({ hasSelectedSite }: { hasSelectedSite: boolean }): JSX.Element {
+ const { activeDashboardTemplate } = useValues(newDashboardLogic)
+ const theDashboardTemplateVariablesLogic = dashboardTemplateVariablesLogic({
+ variables: activeDashboardTemplate?.variables || [],
+ })
+ const { variables, activeVariableIndex } = useValues(theDashboardTemplateVariablesLogic)
+ const { setVariables, setActiveVariableIndex } = useActions(theDashboardTemplateVariablesLogic)
+
+ // TODO: onboarding-dashboard-templates: this is a hack, I'm not sure why it's not set properly initially.
+ useEffect(() => {
+ setVariables(activeDashboardTemplate?.variables || [])
+ }, [activeDashboardTemplate])
+
+ return (
+
+
({
+ key: v.id,
+ header: (
+
+ {v.name}
+ {v.touched && }
+
+ ),
+ content: ,
+ className: 'p-4 bg-white',
+ onHeaderClick: () => {
+ setActiveVariableIndex(i)
+ },
+ }))}
+ embedded
+ size="small"
+ />
+
+ )
+}
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index 61d6072f17d21..d1aaa92a6007f 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -1800,6 +1800,7 @@ export interface DashboardTemplateVariableType {
type: 'event'
default: Record
required: boolean
+ touched?: boolean
}
export type DashboardLayoutSize = 'sm' | 'xs'