Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(onboarding): project switcher and mobile friendly #18634

Merged
merged 14 commits into from
Nov 15, 2023
Merged
30 changes: 29 additions & 1 deletion frontend/src/layout/navigation/TopBar/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,19 @@
import { FEATURE_FLAGS } from 'lib/constants'
import { NotebookButton } from '~/layout/navigation/TopBar/NotebookButton'
import { ActivationSidebarToggle } from 'lib/components/ActivationSidebar/ActivationSidebarToggle'
import { organizationLogic } from 'scenes/organizationLogic'
import { LemonButtonWithDropdown, Lettermark } from '@posthog/lemon-ui'
import { ProjectSwitcherOverlay } from '../ProjectSwitcher'
import { topBarLogic } from './topBarLogic'

export function TopBar(): JSX.Element {
const { isSideBarShown, noSidebar, minimalTopBar, mobileLayout } = useValues(navigationLogic)
const { toggleSideBarBase, toggleSideBarMobile } = useActions(navigationLogic)
const { groupNamesTaxonomicTypes } = useValues(groupsModel)
const { featureFlags } = useValues(featureFlagLogic)
const { currentOrganization } = useValues(organizationLogic)
const { isProjectSwitcherShown } = useValues(topBarLogic)
const { toggleProjectSwitcher, hideProjectSwitcher } = useActions(topBarLogic)

const hasNotebooks = !!featureFlags[FEATURE_FLAGS.NOTEBOOKS]

Expand Down Expand Up @@ -71,11 +78,32 @@
)}
</div>
<div className="TopBar__segment TopBar__segment--right">
{!minimalTopBar && (
{!minimalTopBar ? (
<>
{hasNotebooks && <NotebookButton />}
<NotificationBell />
</>
) : (
currentOrganization?.teams &&
currentOrganization.teams.length > 1 && (
<div>
<LemonButtonWithDropdown

Check warning on line 90 in frontend/src/layout/navigation/TopBar/TopBar.tsx

View workflow job for this annotation

GitHub Actions / Code quality checks

<LemonButtonWithDropdown> is forbidden, use <LemonMenu> with a <LemonButton> child instead
icon={<Lettermark name={currentOrganization?.name} />}
onClick={() => toggleProjectSwitcher()}
dropdown={{
visible: isProjectSwitcherShown,
onClickOutside: hideProjectSwitcher,
overlay: <ProjectSwitcherOverlay onClickInside={hideProjectSwitcher} />,
actionable: true,
placement: 'top-end',
}}
type="secondary"
fullWidth
>
<span className="text-muted">Switch project</span>
</LemonButtonWithDropdown>
</div>
)
)}
<HelpButton />
<SitePopover />
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/layout/navigation/TopBar/topBarLogic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { actions, kea, reducers, path } from 'kea'

import type { topBarLogicType } from './topBarLogicType'

export const topBarLogic = kea<topBarLogicType>([
path(['layout', 'navigation', 'TopBar', 'topBarLogic']),
actions({
toggleProjectSwitcher: true,
hideProjectSwitcher: true,
}),
reducers({
isProjectSwitcherShown: [
false,
{
toggleProjectSwitcher: (state) => !state,
hideProjectSwitcher: () => false,
},
],
}),
])
2 changes: 1 addition & 1 deletion frontend/src/scenes/billing/BillingHero.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@
.BillingHero__hog__img {
height: 200px;
width: 200px;
margin: -20px -30px;
margin: -20px 0;
}
13 changes: 9 additions & 4 deletions frontend/src/scenes/billing/BillingHero.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { BlushingHog } from 'lib/components/hedgehogs'
import './BillingHero.scss'
import useResizeObserver from 'use-resize-observer'

export const BillingHero = (): JSX.Element => {
const { width, ref: billingHeroRef } = useResizeObserver()

return (
<div className="BillingHero">
<div className="BillingHero" ref={billingHeroRef}>
<div className="p-4">
<p className="text-xs uppercase my-0">How pricing works</p>
<h1 className="ingestion-title">Get the whole hog.</h1>
Expand All @@ -13,9 +16,11 @@ export const BillingHero = (): JSX.Element => {
limits as low as $0 to control spend.
</p>
</div>
<div className="BillingHero__hog">
<BlushingHog className="BillingHero__hog__img" />
</div>
{width && width > 500 && (
<div className="BillingHero__hog shrink-0">
<BlushingHog className="BillingHero__hog__img" />
</div>
)}
</div>
)
}
4 changes: 4 additions & 0 deletions frontend/src/scenes/billing/PlanComparison.scss
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ table.PlanComparison {
&.PlanTable__th__feature {
padding: 0.75rem 1.25rem 0.75rem 3.25rem;
font-weight: 600;

&.PlanTable__th__feature--reduced_padding {
padding: 0.75rem 1.25rem;
}
}

&.PlanTable__th__last-feature {
Expand Down
26 changes: 19 additions & 7 deletions frontend/src/scenes/billing/PlanComparison.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import { convertLargeNumberToWords, getUpgradeProductLink } from './billing-utils'
import { billingLogic } from './billingLogic'
import { getProductIcon } from 'scenes/products/Products'
import useResizeObserver from 'use-resize-observer'

export function PlanIcon({
feature,
Expand All @@ -27,7 +28,7 @@ export function PlanIcon({
</>
) : feature.limit ? (
<>
<IconWarning className={`text-warning mx-4 ${className}`} />
<IconWarning className={`text-warning mx-4 shrink-0 ${className}`} />
{feature.limit &&
`${convertLargeNumberToWords(feature.limit, null)} ${feature.unit && feature.unit}${
timeDenominator ? `/${timeDenominator}` : ''
Expand All @@ -36,7 +37,7 @@ export function PlanIcon({
</>
) : (
<>
<IconCheckmark className={`text-success mx-4 ${className}`} />
<IconCheckmark className={`text-success mx-4 shrink-0 ${className}`} />
{feature.note}
</>
)}
Expand All @@ -48,6 +49,7 @@ const getProductTiers = (
plan: BillingV2PlanType,
product: BillingProductV2Type | BillingProductV2AddonType
): JSX.Element => {
const { width, ref: tiersRef } = useResizeObserver()
const tiers = plan?.tiers

const allTierPrices = tiers?.map((tier) => parseFloat(tier.unit_amount_usd))
Expand All @@ -59,7 +61,8 @@ const getProductTiers = (
tiers?.map((tier, i) => (
<div
key={`${plan.key}-${product.type}-${tier.up_to}`}
className="flex justify-between items-center"
className={`flex ${width && width < 100 ? 'flex-col mb-2' : ' justify-between items-center'}`}
ref={tiersRef}
>
<span className="text-xs">
{convertLargeNumberToWords(tier.up_to, tiers[i - 1]?.up_to, true, product.unit)}
Expand All @@ -72,7 +75,11 @@ const getProductTiers = (
</div>
))
) : product?.free_allocation ? (
<div key={`${plan.key}-${product.type}-tiers`} className="flex justify-between items-center">
<div
key={`${plan.key}-${product.type}-tiers`}
className={`flex ${width && width < 100 ? 'flex-col mb-2' : ' justify-between items-center'}`}
ref={tiersRef}
>
<span className="text-xs">
Up to {convertLargeNumberToWords(product?.free_allocation, null)} {product?.unit}s/mo
</span>
Expand All @@ -97,6 +104,7 @@ export const PlanComparison = ({
const fullyFeaturedPlan = plans[plans.length - 1]
const { reportBillingUpgradeClicked } = useActions(eventUsageLogic)
const { redirectPath, billing } = useValues(billingLogic)
const { width, ref: planComparisonRef } = useResizeObserver()

const upgradeButtons = plans?.map((plan) => {
return (
Expand Down Expand Up @@ -132,7 +140,7 @@ export const PlanComparison = ({
})

return (
<table className="PlanComparison w-full table-fixed">
<table className="PlanComparison w-full table-fixed" ref={planComparisonRef}>
<thead>
<tr>
<td />
Expand Down Expand Up @@ -231,8 +239,8 @@ export const PlanComparison = ({
>
<th
className={`text-muted PlanTable__th__feature ${
i == fullyFeaturedPlan?.features?.length - 1 ? 'PlanTable__th__last-feature' : ''
}`}
width && width < 600 && 'PlanTable__th__feature--reduced_padding'
} ${i == fullyFeaturedPlan?.features?.length - 1 ? 'PlanTable__th__last-feature' : ''}`}
>
<Tooltip title={feature.description}>{feature.name}</Tooltip>
</th>
Expand Down Expand Up @@ -283,6 +291,10 @@ export const PlanComparison = ({
<tr key={`tr-${feature.key}`}>
<th
className={`text-muted PlanTable__th__feature ${
width &&
width < 600 &&
'PlanTable__th__feature--reduced_padding'
} ${
// If this is the last feature in the list, add a class to add padding to the bottom of
// the cell (which makes the whole row have the padding)
i ==
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/scenes/ingestion/ingestionLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ export const ingestionLogic = kea<ingestionLogicType>([
},
],
isDemoProject: [
teamLogic.values.currentTeam?.is_demo as null | boolean,
false as null | boolean,
{
setState: (_, { isDemoProject }) => isDemoProject,
},
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/scenes/onboarding/OnboardingBillingStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const OnboardingBillingStep = ({
reportBillingUpgradeClicked(product.type)
}}
>
Upgrade to paid
Subscribe
</LemonButton>
)
}
Expand Down
31 changes: 11 additions & 20 deletions frontend/src/scenes/onboarding/OnboardingOtherProductsStep.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { LemonButton, LemonCard } from '@posthog/lemon-ui'
import { useWindowSize } from 'lib/hooks/useWindowSize'
import { OnboardingStep } from './OnboardingStep'
import { OnboardingStepKey, onboardingLogic } from './onboardingLogic'
import { useActions, useValues } from 'kea'
import { getProductIcon } from 'scenes/products/Products'
import { ProductCard } from 'scenes/products/Products'

export const OnboardingOtherProductsStep = ({
stepKey = OnboardingStepKey.OTHER_PRODUCTS,
Expand All @@ -11,6 +11,8 @@ export const OnboardingOtherProductsStep = ({
}): JSX.Element => {
const { product, suggestedProducts } = useValues(onboardingLogic)
const { completeOnboarding } = useActions(onboardingLogic)
const { width } = useWindowSize()
const horizontalCard = width && width >= 640

return (
<OnboardingStep
Expand All @@ -20,26 +22,15 @@ export const OnboardingOtherProductsStep = ({
continueOverride={<></>}
stepKey={stepKey}
>
<div className="flex flex-col gap-y-6 my-6">
<div className="flex flex-col gap-y-6 my-6 items-center">
{suggestedProducts?.map((suggestedProduct) => (
<LemonCard
className="flex items-center justify-between"
hoverEffect={false}
<ProductCard
product={suggestedProduct}
key={suggestedProduct.type}
>
<div className="flex items-center">
<div className="mr-4">{getProductIcon(suggestedProduct.icon_key, 'text-2xl')}</div>
<div>
<h3 className="font-bold mb-0">{suggestedProduct.name}</h3>
<p className="m-0">{suggestedProduct.description}</p>
</div>
</div>
<div className="justify-self-end min-w-30 flex justify-end">
<LemonButton type="primary" onClick={() => completeOnboarding(suggestedProduct.type)}>
Get started
</LemonButton>
</div>
</LemonCard>
getStartedActionOverride={() => completeOnboarding(suggestedProduct.type)}
orientation={horizontalCard ? 'horizontal' : 'vertical'}
className="w-full"
/>
))}
</div>
</OnboardingStep>
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/scenes/onboarding/OnboardingStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const OnboardingStep = ({
showSkip = false,
onSkip,
continueOverride,
backActionOverride,
}: {
stepKey: OnboardingStepKey
title: string
Expand All @@ -22,6 +23,7 @@ export const OnboardingStep = ({
showSkip?: boolean
onSkip?: () => void
continueOverride?: JSX.Element
backActionOverride?: () => void
}): JSX.Element => {
const { hasNextStep, hasPreviousStep } = useValues(onboardingLogic)
const { completeOnboarding, goToNextStep, goToPreviousStep } = useActions(onboardingLogic)
Expand All @@ -39,14 +41,20 @@ export const OnboardingStep = ({
<div className="mb-4">
<LemonButton
icon={<IconArrowLeft />}
onClick={() => (hasPreviousStep ? goToPreviousStep() : router.actions.push(urls.products()))}
onClick={() =>
backActionOverride
? backActionOverride()
: hasPreviousStep
? goToPreviousStep()
: router.actions.push(urls.products())
}
>
Back
</LemonButton>
</div>
}
>
<div className="w-md">
<div className="max-w-md">
<h1 className="font-bold">{title}</h1>
<p>{subtitle}</p>
{children}
Expand Down
34 changes: 30 additions & 4 deletions frontend/src/scenes/onboarding/sdks/SDKs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { useEffect } from 'react'
import React from 'react'
import { SDKInstructionsMap } from '~/types'
import { InviteMembersButton } from '~/layout/navigation/TopBar/SitePopover'
import { IconArrowLeft } from '@posthog/icons'
import { useWindowSize } from 'lib/hooks/useWindowSize'

export function SDKs({
usersAction,
Expand All @@ -20,23 +22,37 @@ export function SDKs({
subtitle?: string
stepKey?: OnboardingStepKey
}): JSX.Element {
const { setSourceFilter, setSelectedSDK, setAvailableSDKInstructionsMap } = useActions(sdksLogic)
const { sourceFilter, sdks, selectedSDK, sourceOptions, showSourceOptionsSelect } = useValues(sdksLogic)
const { setSourceFilter, setSelectedSDK, setAvailableSDKInstructionsMap, setShowSideBySide, setPanel } =
useActions(sdksLogic)
const { sourceFilter, sdks, selectedSDK, sourceOptions, showSourceOptionsSelect, showSideBySide, panel } =
useValues(sdksLogic)
const { productKey } = useValues(onboardingLogic)
const { width } = useWindowSize()
const minimumSideBySideSize = 768

useEffect(() => {
setAvailableSDKInstructionsMap(sdkInstructionMap)
}, [])

useEffect(() => {
width && setShowSideBySide(width > minimumSideBySideSize)
}, [width])

return (
<OnboardingStep
title={`Where are you ${usersAction || 'collecting data'} from?`}
subtitle={subtitle || 'Pick one or two to start and add more sources later.'}
stepKey={stepKey}
continueOverride={!showSideBySide && panel === 'options' ? <></> : undefined}
backActionOverride={!showSideBySide && panel === 'instructions' ? () => setPanel('options') : undefined}
>
<LemonDivider className="my-8" />
<div className="flex gap-x-8 mt-8">
<div className={`flex flex-col gap-y-2 flex-wrap gap-x-4 min-w-50 w-50`}>
<div
className={`flex-col gap-y-2 flex-wrap gap-x-4 ${showSideBySide && 'min-w-50 w-50'} ${
!showSideBySide && panel !== 'options' ? 'hidden' : 'flex'
}`}
>
{showSourceOptionsSelect && (
<LemonSelect
allowClear
Expand Down Expand Up @@ -69,7 +85,17 @@ export function SDKs({
</LemonCard>
</div>
{selectedSDK && productKey && !!sdkInstructionMap[selectedSDK.key] && (
<div className="shrink min-w-8">
<div className={`shrink min-w-8 ${!showSideBySide && panel !== 'instructions' ? 'hidden' : ''}`}>
{!showSideBySide && (
<LemonButton
icon={<IconArrowLeft />}
onClick={() => setPanel('options')}
className="mb-8"
type="secondary"
>
View all SDKs
</LemonButton>
)}
<SDKSnippet sdk={selectedSDK} sdkInstructions={sdkInstructionMap[selectedSDK.key]} />
</div>
)}
Expand Down
Loading
Loading