Skip to content

Commit

Permalink
finish component improvements, add impact and quote list
Browse files Browse the repository at this point in the history
  • Loading branch information
jsladerman committed Nov 11, 2024
1 parent c0912ea commit 183b9de
Show file tree
Hide file tree
Showing 11 changed files with 681 additions and 83 deletions.
4 changes: 2 additions & 2 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import useMobileDetect from 'use-mobile-detect-hook'

import { directusClient } from '@src/apollo-client'
import { BareModal } from '@src/components/BareModal'
import { ImpactCardSection } from '@src/components/custom-page/ImpactCardSection'
import { FooterVariant } from '@src/components/FooterFull'
import { CircleEmbellishment } from '@src/components/layout/CircleEmbellishment'
import { GradientBG } from '@src/components/layout/GradientBG'
import { HeaderPad } from '@src/components/layout/HeaderPad'
import ArticleSection from '@src/components/page-sections/articleSection'
import { ImpactCardSection } from '@src/components/page-sections/ImpactCardSection'
import { QuoteSection } from '@src/components/page-sections/QuoteSection'
import { HomePageHero } from '@src/components/PageHeros'
import { CenteredSectionHead } from '@src/components/SectionHeads'
Expand Down Expand Up @@ -331,7 +331,7 @@ export default function Index({
title="Delivering value to DevOps and Platform Engineering teams"
quotes={quotes ?? []}
/>
<ImpactCardSection />
<ImpactCardSection impactComponent={null} />
</StandardPageWidth>
</div>
<HomepageFeaturesSection />
Expand Down
39 changes: 32 additions & 7 deletions pages/kubecon.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { Button, CalendarIcon } from '@pluralsh/design-system'
import { Button, CalendarIcon, IconFrame } from '@pluralsh/design-system'
import Image from 'next/image'
import Link from 'next/link'

import styled from 'styled-components'

import { RepeatingLogoSC } from '@src/components/custom-page/common'
import { TextColumnWithIcon } from '@src/components/custom-page/MultiColumnText'
import { ImpactCardSection } from '@src/components/custom-page/ImpactCardSection'
import { icons } from '@src/components/custom-page/MultiColumnText'
import { FooterVariant } from '@src/components/FooterFull'
import { KubeconHeader, handleDownloadICS } from '@src/components/Kubecon'
import { StandardPageWidth } from '@src/components/layout/LayoutHelpers'
import { ImpactCardSection } from '@src/components/page-sections/ImpactCardSection'
import {
Body1,
Hero1,
Hero2,
OverlineLabel,
ResponsiveText,
Subtitle1,
Title1,
} from '@src/components/Typography'
import getPricing from '@src/data/getPricing'
Expand Down Expand Up @@ -120,19 +121,19 @@ export default function KubeCon() {
height={1432}
/>
<div className="flex flex-col gap-xlarge pb-large pt-xxlarge lg:flex-row">
<TextColumnWithIcon
<TextColumnWithIconAlt
heading="Experience live demos"
bodyText="See Plural in action and explore our platform's capabilities"
icon="StackRun"
/>
<div className="pt-xxlarge">
<TextColumnWithIcon
<TextColumnWithIconAlt
heading="Meet our experts"
bodyText="Chat with our team and Founders, about your Kubernetes management challenges — and how we can help!"
icon="People"
/>
</div>
<TextColumnWithIcon
<TextColumnWithIconAlt
heading="Exclusive swag"
bodyText="Swing by and pick up Plural swag we made exclusively for KubeCon 2024"
icon="MagicWand"
Expand All @@ -143,7 +144,7 @@ export default function KubeCon() {
</WhereToFindUsSection>
<StandardPageWidth>
<div className="flex w-full flex-col items-center py-xxxxlarge">
<ImpactCardSection />
<ImpactCardSection impactComponent={null} />
<Button
large
primary
Expand Down Expand Up @@ -321,3 +322,27 @@ const SessionInfoListSC = styled.div`
margin-bottom: 6px;
}
`

function TextColumnWithIconAlt({
heading,
bodyText,
icon,
}: {
heading: string
bodyText: string
icon: string
}) {
const Icon = icons[`${icon}Icon`] ?? icons.KubernetesIcon

return (
<div className="flex flex-col items-center gap-medium text-center">
<IconFrame
size="xlarge"
type="floating"
icon={<Icon color="icon-light" />}
/>
<Subtitle1>{heading}</Subtitle1>
<Body1 $color="text-light">{bodyText}</Body1>
</div>
)
}
4 changes: 2 additions & 2 deletions src/components/PageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ function MinHeader() {
>
<PluralLogoFull height={32} />
</a>
<div className="flex gap-medium">
{/* <div className="flex gap-medium">
<Button
floating
as="a"
Expand All @@ -159,7 +159,7 @@ function MinHeader() {
>
Book a demo
</Button>
</div>
</div> */}
</MinHeaderSC>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { InfoOutlineIcon, Tooltip } from '@pluralsh/design-system'

import styled, { useTheme } from 'styled-components'

import { type OurImpactComponentFragment } from '@src/generated/graphqlDirectus'
import { useMousePosition } from '@src/hooks/useMousePosition'

import { CircleEmbellishment } from '../layout/CircleEmbellishment'
Expand All @@ -20,12 +21,16 @@ type ImpactCardProps = {
tooltipPlacement?: 'top' | 'bottom'
}

export function ImpactCardSection() {
export function ImpactCardSection({
impactComponent,
}: {
impactComponent: Nullable<OurImpactComponentFragment>
}) {
return (
<div className="flex w-full flex-col items-center gap-xxlarge">
<ResponsiveText textStyles={{ '': 'mHero2' }}>Our impact</ResponsiveText>
<ImpactCardsWrapperSC>
{impactCards.map((cardProps, index) => (
{getImpactCards(impactComponent).map((cardProps, index) => (
<ImpactCard
key={index}
{...cardProps}
Expand Down Expand Up @@ -171,38 +176,50 @@ const TooltipTextSC = styled.p(({ theme }) => ({
},
}))

const impactCards: ImpactCardProps[] = [
{
metric: '88%',
subtitle: 'Reduction in operational costs',
embellishment: 'top-left',
borderGradientDir: 'to right',
tooltipText:
'Plural provides a single pane of glass and integrated suite of tools to manage the entire lifecycle of your Kubernetes fleet, giving engineers all the tools they need to do more with less. These cost savings free up budget for innovation, freeing operational challenges into financial victories and allowing your team to scale.',
},
{
metric: '95%',
subtitle: 'Reduction in day-2 operations',
tooltipText:
"Plural's comprehensive feature set allows teams to take care of mundane maintenance tasks such as upgrades in a fraction of the time.",
borderGradientDir: 'to left',
},
{
metric: '50%',
subtitle: 'Increased bandwidth for your engineers',
borderGradientDir: 'to right',
tooltipText:
'Free up 50% more of your engineering capacity for high-impact, strategic projects. Plural streamlines repetitive tasks like upgrade management, infrastructure provisioning and scaling, allowing your engineers to concentrate on value-driven initiatives that propel your company forward.',
},
{
metric: '~30x',
subtitle: 'ROI over 3 years',
embellishment: 'bottom-right',
borderGradientDir: 'to left',
tooltipText:
'Through optimized resource utilization, reduced downtime, and efficient infrastructure management, Plural delivers a significant return on investment, enabling your platform team to build, innovate, and iterate faster without the typical operational bottlenecks.',
},
]
function getImpactCards(
impactComponent: Nullable<OurImpactComponentFragment>
): ImpactCardProps[] {
return [
{
metric: impactComponent?.top_left_metric ?? '88%',
subtitle:
impactComponent?.top_left_subtitle ?? 'Reduction in operational costs',
embellishment: 'top-left',
borderGradientDir: 'to right',
tooltipText:
impactComponent?.top_left_tooltip ??
'Plural provides a single pane of glass and integrated suite of tools to manage the entire lifecycle of your Kubernetes fleet, giving engineers all the tools they need to do more with less. These cost savings free up budget for innovation, freeing operational challenges into financial victories and allowing your team to scale.',
},
{
metric: impactComponent?.top_right_metric ?? '95%',
subtitle:
impactComponent?.top_right_subtitle ?? 'Reduction in day-2 operations',
tooltipText:
impactComponent?.top_right_tooltip ??
"Plural's comprehensive feature set allows teams to take care of mundane maintenance tasks such as upgrades in a fraction of the time.",
borderGradientDir: 'to left',
},
{
metric: impactComponent?.bottom_left_metric ?? '50%',
subtitle:
impactComponent?.bottom_left_subtitle ??
'Increased bandwidth for your engineers',
borderGradientDir: 'to right',
tooltipText:
impactComponent?.bottom_left_tooltip ??
'Free up 50% more of your engineering capacity for high-impact, strategic projects. Plural streamlines repetitive tasks like upgrade management, infrastructure provisioning and scaling, allowing your engineers to concentrate on value-driven initiatives that propel your company forward.',
},
{
metric: impactComponent?.bottom_right_metric ?? '~30x',
subtitle: impactComponent?.bottom_right_subtitle ?? 'ROI over 3 years',
embellishment: 'bottom-right',
borderGradientDir: 'to left',
tooltipText:
impactComponent?.bottom_right_tooltip ??
'Through optimized resource utilization, reduced downtime, and efficient infrastructure management, Plural delivers a significant return on investment, enabling your platform team to build, innovate, and iterate faster without the typical operational bottlenecks.',
},
]
}

const getEmbelishmentPosition = (
embellishment: ImpactCardProps['embellishment']
Expand Down
28 changes: 26 additions & 2 deletions src/components/custom-page/LargeImage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Button, Flex } from '@pluralsh/design-system'
import Link from 'next/link'

import styled from 'styled-components'

import { type LargeImageComponentFragment } from '@src/generated/graphqlDirectus'

import { Body2, OverlineLabel, Title1 } from '../Typography'
Expand All @@ -14,13 +16,17 @@ export function LargeImage({
cta_text: ctaText,
cta_url: ctaUrl,
media_type: mediaType,
image_position: imagePosition,
image,
video_url: videoUrl,
form,
}: LargeImageComponentFragment) {
return (
<div className="mx-xxxxxxlarge">
<Flex gap="xxxlarge">
<div className="mx-xxxxxxlarge flex flex-col gap-xxxxlarge">
<Flex
gap="xxxlarge"
flexDirection={imagePosition === 'left' ? 'row' : 'row-reverse'}
>
<Multimedia
mediaType={mediaType}
image={image}
Expand Down Expand Up @@ -49,6 +55,24 @@ export function LargeImage({
)}
</Flex>
</Flex>
<GradientDividerLine
$toDirection={imagePosition === 'left' ? 'right' : 'left'}
/>
</div>
)
}

const GradientDividerLine = styled.div<{ $toDirection: 'left' | 'right' }>(
({ $toDirection }) => ({
width: '100%',
height: '1px',
opacity: 0.5,
background: `linear-gradient(
to ${$toDirection},
#fff 13%,
#ccc 40.5%,
#999 63.5%,
transparent 93.5%
)`,
})
)
18 changes: 8 additions & 10 deletions src/components/custom-page/MultiColumnText.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { IconFrame } from '@pluralsh/design-system'

import createIcon from '@pluralsh/design-system/dist/components/icons/createIcon'
import * as designSystemIcons from '@pluralsh/design-system/dist/icons'

Expand All @@ -9,7 +7,7 @@ import { type MultiColumnTextComponentFragment } from '@src/generated/graphqlDir
import { Body1, Subtitle1 } from '../Typography'

// TODO: StackRun icon very much temporary, need to update the DS in this repo soon
const icons = {
export const icons = {
...productNavIcons,
...designSystemIcons,
StackRunIcon: createIcon(({ size, color }) => (
Expand Down Expand Up @@ -42,7 +40,7 @@ const icons = {

export function MultiColumnText({ columns }: MultiColumnTextComponentFragment) {
return (
<div className="mx-xxxlarge flex gap-xlarge pt-medium">
<div className="mx-xxxlarge flex gap-xlarge border-b border-border-input pb-xxlarge">
{columns?.map((c, index) => {
const heading = c?.rich_text_columns_id?.heading
const bodyText = c?.rich_text_columns_id?.body_text
Expand All @@ -60,7 +58,7 @@ export function MultiColumnText({ columns }: MultiColumnTextComponentFragment) {
)
}

export function TextColumnWithIcon({
function TextColumnWithIcon({
heading,
bodyText,
icon,
Expand All @@ -72,11 +70,11 @@ export function TextColumnWithIcon({
const Icon = icons[`${icon}Icon`] ?? icons.KubernetesIcon

return (
<div className="flex flex-col items-center gap-medium text-center">
<IconFrame
size="xlarge"
type="floating"
icon={<Icon color="icon-light" />}
<div className="flex flex-col items-start gap-medium pb-large pt-medium">
<Icon
color="icon-light"
style={{ padding: 8 }}
size={32}
/>
<Subtitle1>{heading}</Subtitle1>
<Body1 $color="text-light">{bodyText}</Body1>
Expand Down
19 changes: 12 additions & 7 deletions src/components/custom-page/Multimedia.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { type ComponentProps } from 'react'
import { type ComponentPropsWithRef } from 'react'

import { Code } from '@pluralsh/design-system'

import styled from 'styled-components'

import { getImageUrl } from '@src/consts/routes'
import { type ImageFileFragment } from '@src/generated/graphqlDirectus'

Expand All @@ -21,14 +23,11 @@ export function Multimedia({
videoUrl: Nullable<string>
form: Nullable<string>
aspectRatio?: string
} & ComponentProps<'div'>) {
} & ComponentPropsWithRef<'div'>) {
const imageUrl = getImageUrl(image)

return (
<div
className="m-auto flex-1"
{...props}
>
<MultimediaWrapperSC {...props}>
{mediaType === 'image' ? (
imageUrl && (
<ImageAspectRatio
Expand All @@ -44,6 +43,12 @@ export function Multimedia({
) : mediaType === 'form' ? (
<Code css={{ overflow: 'auto', maxHeight: '500px' }}>{form ?? ''}</Code>
) : null}
</div>
</MultimediaWrapperSC>
)
}

const MultimediaWrapperSC = styled.div(({ theme }) => ({
margin: 'auto',
flex: 1,
boxShadow: theme.boxShadows.modal,
}))
16 changes: 16 additions & 0 deletions src/components/custom-page/QuoteCarousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { type QuoteCarouselComponentFragment } from '@src/generated/graphqlDirectus'
import { normalizeQuotes } from '@src/utils/normalizeQuotes'

import { QuoteSection } from '../page-sections/QuoteSection'

export function QuoteCarousel({
title,
quotes,
}: QuoteCarouselComponentFragment) {
return (
<QuoteSection
title={title ?? ''}
quotes={normalizeQuotes(quotes) ?? []}
/>
)
}
Loading

0 comments on commit 183b9de

Please sign in to comment.