-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
Copy pathProducts.tsx
163 lines (153 loc) · 7.52 KB
/
Products.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import * as Icons from '@posthog/icons'
import { IconArrowRight, IconCheckCircle } from '@posthog/icons'
import { LemonButton, LemonLabel, LemonSelect, Link, Tooltip } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { useActions, useValues } from 'kea'
import { router } from 'kea-router'
import { LemonCard } from 'lib/lemon-ui/LemonCard/LemonCard'
import { getProductUri, onboardingLogic } from 'scenes/onboarding/onboardingLogic'
import { availableOnboardingProducts } from 'scenes/onboarding/utils'
import { SceneExport } from 'scenes/sceneTypes'
import { inviteLogic } from 'scenes/settings/organization/inviteLogic'
import { teamLogic } from 'scenes/teamLogic'
import { OnboardingProduct, ProductKey } from '~/types'
import { productsLogic } from './productsLogic'
export const scene: SceneExport = {
component: Products,
}
export function getProductIcon(color: string, iconKey?: string | null, className?: string): JSX.Element {
const Icon = Icons[iconKey || 'IconLogomark']
return <Icon className={className} color={color} />
}
export function SelectableProductCard({
product,
productKey,
onClick,
orientation = 'vertical',
className,
selected = false,
}: {
product: OnboardingProduct
productKey: string
onClick: () => void
orientation?: 'horizontal' | 'vertical'
className?: string
selected?: boolean
}): JSX.Element {
const { currentTeam } = useValues(teamLogic)
const { replayLandingPage } = useValues(onboardingLogic)
const onboardingCompleted = currentTeam?.has_completed_onboarding_for?.[productKey]
const vertical = orientation === 'vertical'
return (
<LemonCard
data-attr={`${productKey}-onboarding-card`}
className={clsx('flex justify-center cursor-pointer', vertical ? 'flex-col' : 'items-center', className)}
key={productKey}
onClick={onClick}
focused={selected}
>
{onboardingCompleted && (
<Tooltip
title="You've already set up this product. Click to return to this product's page."
placement="right"
>
<div
className="relative"
onClick={(e) => {
e.stopPropagation()
router.actions.push(getProductUri(productKey as ProductKey, replayLandingPage))
}}
data-attr={`return-to-${productKey}`}
>
<IconCheckCircle className="absolute top-0 right-0" color="green" />
</div>
</Tooltip>
)}
<div className="grid grid-rows-[repeat(2,_48px)] justify-items-center select-none">
<div className="self-center">{getProductIcon(product.iconColor, product.icon, 'text-2xl')}</div>
<div className="font-bold text-center self-start text-md">{product.name}</div>
</div>
</LemonCard>
)
}
export function Products(): JSX.Element {
const { showInviteModal } = useActions(inviteLogic)
const { toggleSelectedProduct, setFirstProductOnboarding, handleStartOnboarding } = useActions(productsLogic)
const { selectedProducts, firstProductOnboarding } = useValues(productsLogic)
return (
<div className="flex flex-col flex-1 w-full h-full p-4 items-center justify-center bg-primary">
<>
<div className="flex flex-col justify-center flex-grow items-center">
<div className="mb-2">
<h2 className="text-center text-4xl">Which products would you like to use?</h2>
<p className="text-center">
Don't worry – you can pick more than one! Please select all that apply.
</p>
</div>
<div className="flex flex-col-reverse sm:flex-col gap-6 md:gap-12 justify-center items-center w-full max-w-[720px]">
<div className="flex flex-wrap gap-4 items-center justify-center">
{Object.keys(availableOnboardingProducts).map((productKey) => (
<SelectableProductCard
product={
availableOnboardingProducts[
productKey as keyof typeof availableOnboardingProducts
]
}
key={productKey}
productKey={productKey}
onClick={() => {
toggleSelectedProduct(productKey as ProductKey)
}}
className="w-[160px]"
selected={selectedProducts.includes(productKey as ProductKey)}
/>
))}
</div>
<div className="flex gap-2 justify-center items-center">
{selectedProducts.length > 1 ? (
<>
<LemonLabel>Start first with</LemonLabel>
<LemonSelect
value={firstProductOnboarding}
options={selectedProducts.map((productKey) => ({
label: availableOnboardingProducts[productKey].name,
value: productKey,
}))}
onChange={(value) => value && setFirstProductOnboarding(value)}
placeholder="Select a product"
className="bg-surface-primary"
/>
<LemonButton
sideIcon={<IconArrowRight />}
onClick={handleStartOnboarding}
type="primary"
status="alt"
data-attr="onboarding-continue"
>
Go
</LemonButton>
</>
) : (
<LemonButton
type="primary"
status="alt"
onClick={handleStartOnboarding}
data-attr="onboarding-continue"
sideIcon={<IconArrowRight />}
disabledReason={
selectedProducts.length === 0 ? 'Select a product to start with' : undefined
}
>
Get started
</LemonButton>
)}
</div>
</div>
</div>
<p className="text-center mt-8">
Need help from a team member? <Link onClick={() => showInviteModal()}>Invite them</Link>
</p>
</>
</div>
)
}