From 7ba61dd226a0fcde8ff6a95225f05740edc0cb0c Mon Sep 17 00:00:00 2001 From: Viktor Soroka Date: Mon, 4 Nov 2024 01:27:58 +0200 Subject: [PATCH] Implement changes to on-premises and accelerators pages --- .../CertificationCard/CertificateCard.scss | 3 + .../Certificates/Certificates.scss | 15 +- .../ComparePlans/Columns/Columns.tsx | 5 +- src/components/ComparePlans/ComparePlans.scss | 80 ++++--- src/components/ComparePlans/ComparePlans.tsx | 68 +++--- .../InfoCard/InfoCard.scss} | 2 +- src/components/InfoCard/InfoCard.tsx | 31 +++ src/components/InfoCard/index.ts | 1 + .../OfferPageWrapper/OfferPageWrapper.scss | 167 ++++++++++----- .../OfferPageWrapper/OfferPageWrapper.tsx | 126 ++++++----- .../OfferPageWrapper/TimeScale/TimeScale.scss | 12 +- .../OfferPageWrapper/icons/opensource.svg | 1 + src/components/PricingCard/PricingCard.scss | 53 ++++- src/components/PricingCard/PricingCard.tsx | 79 ++++++- src/components/PricingHero/PricingHero.scss | 26 ++- src/components/PricingHero/PricingHero.tsx | 36 ++-- .../AcceleratorsPage/D4jPage/D4jPage.tsx | 2 +- .../CodeOfConduct/CodeOfConduct.tsx | 29 --- .../CommunityPage/CodeOfConduct/index.ts | 1 - .../CommunityPage/CommunityPage.tsx | 14 +- .../ContactUsDetails/ContactUsDetails.tsx | 6 +- .../ContactUsForm/ContactUsForm.tsx | 2 +- .../CommonRequests/CommonRequests.tsx | 2 +- .../SassPage/PricingCards/PricingCards.scss | 3 +- src/containers/SassPage/SaasPage.tsx | 11 +- src/fragments.ts | 1 - src/hooks/usePricingHeroProps.tsx | 2 +- src/styles/global.scss | 4 - src/svg/deal.inline.svg | 2 + src/svg/headphones.inline.svg | 1 + src/svg/infoIcon.inline.svg | 6 +- src/svg/star.inline.svg | 1 + src/svg/tool.inline.svg | 1 + src/utils/contactUsConfig.ts | 202 +++++------------- src/utils/formatOfferingPlans.ts | 1 - src/utils/types.ts | 1 - 36 files changed, 581 insertions(+), 416 deletions(-) rename src/{containers/CommunityPage/CodeOfConduct/CodeOfConduct.scss => components/InfoCard/InfoCard.scss} (97%) create mode 100644 src/components/InfoCard/InfoCard.tsx create mode 100644 src/components/InfoCard/index.ts create mode 100644 src/components/OfferPageWrapper/icons/opensource.svg delete mode 100644 src/containers/CommunityPage/CodeOfConduct/CodeOfConduct.tsx delete mode 100644 src/containers/CommunityPage/CodeOfConduct/index.ts create mode 100644 src/svg/deal.inline.svg create mode 100644 src/svg/headphones.inline.svg create mode 100644 src/svg/star.inline.svg create mode 100644 src/svg/tool.inline.svg diff --git a/src/components/CertificationCard/CertificateCard.scss b/src/components/CertificationCard/CertificateCard.scss index ca5a5ff13..781cf4ce8 100644 --- a/src/components/CertificationCard/CertificateCard.scss +++ b/src/components/CertificationCard/CertificateCard.scss @@ -39,6 +39,9 @@ } &__certificates { + display: flex; + justify-content: center; + width: 100%; margin-top: 40px; } diff --git a/src/components/CertificationCard/Certificates/Certificates.scss b/src/components/CertificationCard/Certificates/Certificates.scss index 5ef06637c..957290369 100644 --- a/src/components/CertificationCard/Certificates/Certificates.scss +++ b/src/components/CertificationCard/Certificates/Certificates.scss @@ -3,10 +3,19 @@ .certificates { display: flex; - gap: 24px; + gap: 16px; + + @include m.breakpoint(v.$phone-lg) { + gap: 24px; + } svg { - width: 80px; - height: 80px; + width: 64px; + height: 64px; + + @include m.breakpoint(v.$phone-lg) { + width: 80px; + height: 80px; + } } } diff --git a/src/components/ComparePlans/Columns/Columns.tsx b/src/components/ComparePlans/Columns/Columns.tsx index 41a63219c..dfa795652 100644 --- a/src/components/ComparePlans/Columns/Columns.tsx +++ b/src/components/ComparePlans/Columns/Columns.tsx @@ -13,12 +13,11 @@ import '../ComparePlans.scss'; interface ColumnsProps { cols: FormattedComparePlansItemDto['plans']; title?: string; - mobileColumns?: FormattedComparePlansItemDto['plans']; } const getBlocksWith = createBemBlockBuilder(['compare']); -export const Columns: FC = ({ title = '', cols, mobileColumns = [] }) => { +export const Columns: FC = ({ title = '', cols }) => { const isDesktop = useMediaQuery({ query: MEDIA_DESKTOP_SM }); const getMark = (value: boolean) => @@ -56,7 +55,7 @@ export const Columns: FC = ({ title = '', cols, mobileColumns = []
{getRenderedValue()}
diff --git a/src/components/ComparePlans/ComparePlans.scss b/src/components/ComparePlans/ComparePlans.scss index f84aacf1d..1208a4d8d 100644 --- a/src/components/ComparePlans/ComparePlans.scss +++ b/src/components/ComparePlans/ComparePlans.scss @@ -26,14 +26,13 @@ } .compare { - margin-top: 176px; - - @include m.breakpoint(v.$tablet-sm-exact) { - margin-top: 160px; - } + padding-top: 80px; + padding-bottom: 80px; + background: linear-gradient(180deg, #f4f5fa 0%, #fff 100%); @include m.breakpoint(v.$desktop-sm) { - margin-top: 240px; + padding-top: 120px; + padding-bottom: 120px; } .ant-tablet-ghost { @@ -67,6 +66,7 @@ border-radius: 16px; padding-top: 24px; box-shadow: 0 8px 16px rgb(0 45 55 / 0.12); + background: var(--white); } .ant-collapse-no-arrow { @@ -125,6 +125,10 @@ } } + .ant-collapse-item { + background: var(--white); + } + .ant-collapse-item:nth-child(even) { @include m.breakpoint(v.$desktop-sm) { background: #f8fafd; @@ -148,6 +152,16 @@ padding-top: 7px !important; } } + + .ant-collapse-content-box { + padding-top: 0 !important; + + @include m.breakpoint(v.$desktop-sm) { + .ant-collapse-content-box { + padding-top: 7px !important; + } + } + } } } @@ -208,7 +222,7 @@ &__tab { &-data { - margin-top: 19px; + margin-top: 16px; @include m.breakpoint(v.$tablet-sm-exact) { padding: 0 70px; @@ -218,7 +232,7 @@ margin: 16px 0; @include m.breakpoint(v.$tablet-sm-exact) { - margin: 7px 0 12px; + margin: 10px 0 12px; } .compare__row-title-cols { @@ -353,14 +367,11 @@ &-cols { @include m.font-scale(base); - display: flex; - flex: 3; - align-items: center; - justify-content: space-between; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); + width: 100%; @include m.breakpoint(v.$desktop-sm) { - display: flex; - flex: 0 1 auto; width: 65%; } @@ -368,7 +379,7 @@ display: none; @include m.breakpoint(v.$desktop-sm) { - display: flex; + display: grid; } } } @@ -376,8 +387,8 @@ &-col { @include m.font-poppins(v.$fw-medium); - display: inline-block; - width: 30%; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); text-align: center; color: var(--text-primary); @@ -389,6 +400,10 @@ @include m.font-poppins(v.$fw-semi-bold); @include m.font-scale(medium); } + + &:has(> div:empty) { + display: none; + } } } } @@ -397,6 +412,7 @@ @include m.font-poppins(); @include m.font-scale(small); + margin-top: 4px; margin-right: 8px; margin-left: 8px; white-space: pre-wrap; @@ -406,23 +422,25 @@ margin-left: 15px; } + p { + margin: 0; + } + + p + p { + margin-top: 16px; + } + &-anchor { position: relative; display: inline-block; - - &:hover::after { - background-color: var(--text-grey); - } - - &::after { - position: absolute; - bottom: 6px; - left: 0; - width: 100%; - height: 1px; - background-color: var(--text-service); - transition: background-color 0.3s ease; - content: ''; + margin-bottom: -1px; + border-bottom: 2px solid transparent; + color: var(--color-primary-600); + + &:hover { + border-bottom-color: var(--color-primary-600); + transition: all 0.4s ease-out; + color: var(--color-primary-600); } } diff --git a/src/components/ComparePlans/ComparePlans.tsx b/src/components/ComparePlans/ComparePlans.tsx index e43e61cb7..e07395219 100644 --- a/src/components/ComparePlans/ComparePlans.tsx +++ b/src/components/ComparePlans/ComparePlans.tsx @@ -3,7 +3,6 @@ import { useMediaQuery } from 'react-responsive'; import { Collapse } from 'antd'; import { renderRichText } from 'gatsby-source-contentful/rich-text'; import { INLINES } from '@contentful/rich-text-types'; -import isEmpty from 'lodash/isEmpty'; import size from 'lodash/size'; import classNames from 'classnames'; import { @@ -28,13 +27,12 @@ interface ComparePlansProps { const getBlocksWith = createBemBlockBuilder(['compare']); export const ComparePlans: FC = ({ - plans: { sections, columns, mobileColumns, ctas, note }, + plans: { sections, columns, ctas, note }, isCollapsibleOnMobile = true, }) => { const isDesktop = useMediaQuery({ query: MEDIA_DESKTOP_SM }); const { Panel } = Collapse; const [featureColumn, ...plansColumns] = columns; - const [, ...plansColumnsMobile] = mobileColumns; const getRowKey = (sectionIndex: number, itemIndex: number) => `${sections[sectionIndex].title}${sections[sectionIndex].items[itemIndex].name}`; @@ -71,7 +69,7 @@ export const ComparePlans: FC = ({
{!isDesktop && (
- +
)}
@@ -134,39 +132,41 @@ export const ComparePlans: FC = ({ return (
- {!isCollapsable ? ( - <> -
Compare plans
- {comparePlans} - - ) : ( - ( - {isActive - )} - items={[ - { - label: 'Compare plans', - key: 1, - children: comparePlans, - }, - ]} - /> - )} +
+ {!isCollapsable ? ( + <> +
Compare packages
+ {comparePlans} + + ) : ( + ( + {isActive + )} + items={[ + { + label: 'Compare plans', + key: 1, + children: comparePlans, + }, + ]} + /> + )} +
); }; diff --git a/src/containers/CommunityPage/CodeOfConduct/CodeOfConduct.scss b/src/components/InfoCard/InfoCard.scss similarity index 97% rename from src/containers/CommunityPage/CodeOfConduct/CodeOfConduct.scss rename to src/components/InfoCard/InfoCard.scss index b424659cb..d35bb784e 100644 --- a/src/containers/CommunityPage/CodeOfConduct/CodeOfConduct.scss +++ b/src/components/InfoCard/InfoCard.scss @@ -1,7 +1,7 @@ @use 'src/styles/mixins' as m; @use 'src/styles/variables' as v; -.code-of-conduct { +.info-card { display: flex; flex-direction: column; align-items: flex-start; diff --git a/src/components/InfoCard/InfoCard.tsx b/src/components/InfoCard/InfoCard.tsx new file mode 100644 index 000000000..e26083882 --- /dev/null +++ b/src/components/InfoCard/InfoCard.tsx @@ -0,0 +1,31 @@ +import React, { FC } from 'react'; +import classNames from 'classnames'; +import { Link } from '@app/components/Link'; +import { createBemBlockBuilder, LinkDto } from '@app/utils'; + +import './InfoCard.scss'; + +const getBlocksWith = createBemBlockBuilder(['info-card']); + +interface InfoCardProps { + title: string; + description: string; + link: LinkDto; + icon: string; +} + +export const InfoCard: FC = ({ title, description, icon, link }) => ( +
+ +
+ {title} +

{description}

+
+ + {link.title} + +
+); diff --git a/src/components/InfoCard/index.ts b/src/components/InfoCard/index.ts new file mode 100644 index 000000000..c02f55208 --- /dev/null +++ b/src/components/InfoCard/index.ts @@ -0,0 +1 @@ +export * from './InfoCard'; diff --git a/src/components/OfferPageWrapper/OfferPageWrapper.scss b/src/components/OfferPageWrapper/OfferPageWrapper.scss index 17bdeac3b..fb5403173 100644 --- a/src/components/OfferPageWrapper/OfferPageWrapper.scss +++ b/src/components/OfferPageWrapper/OfferPageWrapper.scss @@ -2,21 +2,59 @@ @use 'src/styles/variables' as v; .offer-page-wrapper { - &__pentagons { + &__plans-container { display: flex; flex-direction: column; - justify-content: center; - margin-top: -239px; + align-items: center; + padding-bottom: 80px; + opacity: 1; + transform: none; + + @include m.breakpoint(v.$tablet-sm-exact) { + padding-bottom: 120px; + } + + @include m.breakpoint(v.$desktop-sm) { + padding-right: 0; + padding-left: 0; + } + + h2 { + @include m.font-poppins(v.$fw-bold); + @include m.font-scale(x4-medium); + + margin-top: 88px; + margin-bottom: 0; + text-align: center; + color: var(--white); + + @include m.breakpoint(v.$tablet-sm-exact) { + @include m.font-scale(large); + + margin: 72px 80px 0; + } + } + + .info-card { + margin: 40px 16px 0; + } + } + + &__plans { + display: flex; + flex-flow: column wrap; + place-content: center center; + gap: 40px 24px; + margin-top: 64px; @include m.breakpoint(v.$tablet-sm-exact) { - margin-top: -271px; - margin-bottom: 80px; + margin-top: 40px; } @include m.breakpoint(v.$desktop-sm) { - flex-direction: row; - margin-top: -288px; - margin-bottom: 120px; + flex-flow: row nowrap; + width: 995px; + margin-top: 40px; } button:last-child { @@ -26,17 +64,65 @@ margin-bottom: 0; } } + + .pricing-card { + max-width: 348px; + height: 504px; + + ul { + margin: 0; + } + } + + .pricing-card__description { + height: 48px; + margin-bottom: 24px; + } + + .pricing-card__price-description { + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + color: var(--text-service); + } + + .pricing-card__popular { + border: 1px solid var(--white); + background: var(--graphics-jade); + } + } + + &__plans-topology { + display: flex; + flex-direction: column; + row-gap: 16px; + max-width: 348px; + margin-top: 40px; + + @include m.breakpoint(v.$desktop-sm) { + width: 995px; + max-width: unset; + margin-top: 24px; + } + + > div { + justify-content: start; + } } &__utilization { - padding-left: 16px; - background: var( - --gradients-light-grey, - linear-gradient(180deg, #f4f5fa 0%, rgb(244 245 250 / 0) 100%) - ); + padding: 80px 16px 48px; @include m.breakpoint(v.$tablet-sm-exact) { - padding: 0; + padding-right: 24px; + padding-bottom: 80px; + padding-left: 24px; + } + + @include m.breakpoint(v.$desktop-sm) { + padding-top: 120px; + padding-bottom: 120px; } h2 { @@ -44,7 +130,6 @@ @include m.font-scale(x3-medium); margin: 0; - padding-top: 80px; padding-bottom: 24px; color: var(--text-primary); @@ -52,10 +137,6 @@ padding-bottom: 16px; text-align: center; } - - @include m.breakpoint(v.$desktop-sm) { - padding-top: 120px; - } } &-subtitle { @@ -74,47 +155,39 @@ &__subscription-info { display: flex; - align-items: center; justify-content: center; - padding: 0 16px; color: var(--text-primary); - @include m.breakpoint(v.$tablet-sm-exact) { - padding: 0 32px; - } - @include m.breakpoint(v.$desktop-sm) { padding: 0; text-align: center; } svg { - min-width: 28px; - margin-right: 16px; - - @include m.breakpoint(v.$desktop-sm) { - margin-right: 4px; - } + flex-shrink: 0; + width: 24px; + height: 24px; + margin-top: -2px; + margin-right: 14px; } a { @include m.font-poppins(v.$fw-medium); margin-left: 4px; - color: var(--text-service); + color: var(--color-primary-600); &:hover { - color: var(--text-grey); + color: var(--color-primary-500); } } } &__faq-container { - margin-top: 160px; margin-bottom: 153px; + padding-top: 80px; @include m.breakpoint(v.$tablet-sm-exact) { - margin-top: 200px; margin-bottom: 168px; } @@ -124,25 +197,19 @@ } } - &__trusted-organizations-container { - margin-top: 130px; - - @include m.breakpoint(v.$tablet-sm-exact) { - margin-top: 160px; - } + &__gradient-container { + padding-top: 80px; + padding-bottom: 80px; + background: linear-gradient(180deg, #f4f5fa 0%, #fff 100%); @include m.breakpoint(v.$desktop-sm) { - margin-top: 240px; + padding-top: 120px; } - } - - &__certificates-container { - margin-top: 80px; - margin-bottom: 0; - @include m.breakpoint(v.$tablet-sm-exact) { - margin-top: 120px; - margin-bottom: 80px; + .container { + display: flex; + flex-flow: column; + row-gap: 80px; } } } diff --git a/src/components/OfferPageWrapper/OfferPageWrapper.tsx b/src/components/OfferPageWrapper/OfferPageWrapper.tsx index 3b625ffd5..0835a39f4 100644 --- a/src/components/OfferPageWrapper/OfferPageWrapper.tsx +++ b/src/components/OfferPageWrapper/OfferPageWrapper.tsx @@ -1,4 +1,4 @@ -import React, { FC, ReactNode } from 'react'; +import React, { FC, ReactNode, useRef } from 'react'; import { motion } from 'framer-motion'; import classNames from 'classnames'; import { @@ -14,14 +14,18 @@ import { Banner } from '@app/components/Banner'; import { Link } from '@app/components/Link'; import { PricingHero } from '@app/components/PricingHero'; import { ComparePlans } from '@app/components/ComparePlans'; +import { PricingCard } from '@app/components/PricingCard'; import { Faq } from '@app/components/Faq'; import { CertificationCard } from '@app/components/CertificationCard'; -import InfoIcon from '@app/svg/infoIcon.inline.svg'; +import { InfoCard } from '@app/components/InfoCard'; import { useInView } from '@app/hooks/useInView'; import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; import { useAnimationEnabledForSiblingRoutes } from '@app/hooks/useAnimationEnabledForSiblingRoutes'; +import ToolIcon from '@app/svg/tool.inline.svg'; +import HeadphonesIcon from '@app/svg/headphones.inline.svg'; +import InfoIcon from '@app/svg/infoIcon.inline.svg'; -import { PentagonCard } from './PentagonCard'; +import openSourceIcon from './icons/opensource.svg'; import { TimeScale } from './TimeScale'; import './OfferPageWrapper.scss'; @@ -67,18 +71,26 @@ export const OfferPageWrapper: FC = ({ faqLink, isScaleShifted = false, }) => { - const { buttons, isYearlyPlanType, togglePlanType } = usePricingHeroProps(page); + const { buttons } = usePricingHeroProps(page); const [cardsRef, areCardsInView] = useInView(); + const utilizationRef = useRef(null); const isAnimationEnabled = useAnimationEnabledForSiblingRoutes(); - - const planType = isYearlyPlanType ? 'yearly' : 'quarterly'; - const isPricingPage = page === 'pricing'; - const getCardsAnimation = useMotionEnterAnimation( easeInOutOpacityScaleAnimationProps, isAnimationEnabled, ); + const isPricingPage = page === 'pricing'; + const pricingCardsAnimation = getCardsAnimation({ + isInView: areCardsInView, + delay: 0.6, + additionalEffects: { + hiddenAdditional: { y: 50 }, + enterAdditional: { y: 0 }, + }, + }); + const [openSourcePlan, ...paidPlans] = plans.items; + return ( <> = ({ activeButton={offerType} offerType={offerType} description={description} - switcherProps={{ - isYearlyPlanType, - togglePlanType, - messageInactive: 'Quarterly', - messageActive: 'Yearly (Save 5%)', - }} isAnimationEnabled={isAnimationEnabled} /> - - {plans.items.map((plan, index) => { - const pricingValue = plan.price?.[planType]; - const href = plan.cta.link.url; - const actionLink = plan.isContactUsURLEndsWithPlanType ? `${href}/${planType}` : href; - - return ( - Get a full year of benefits with our service packages +
+ {paidPlans.map(paidPlan => ( + - ); - })} - -
+ ))} +
+
+
+ +
+ Technical Support Points (hours) refer to the duration of the available technical + consultations. +
+
+
+ +
+ Professional Service Points (hours) reflect our specialists' efforts on feature + development, integrations, etc.{' '} + { + event.preventDefault(); + + utilizationRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }); + }} + > + Learn more + +
+
+
+ + + +

Indicative Professional Service Point utilization

{utilizationDescription}
@@ -141,21 +168,16 @@ export const OfferPageWrapper: FC = ({
- {isPricingPage && ( - <> -
+
+
-
-
- +
)}
diff --git a/src/components/PricingCard/PricingCard.scss b/src/components/PricingCard/PricingCard.scss index d2a494d6f..6a4eb956d 100644 --- a/src/components/PricingCard/PricingCard.scss +++ b/src/components/PricingCard/PricingCard.scss @@ -58,7 +58,7 @@ &__title { @include m.font-poppins(v.$fw-semi-bold); - @include m.font-scale(x3-medium); + @include m.font-scale(large); text-align: center; color: var(--dark); @@ -70,7 +70,7 @@ } &__description { - @include m.font-noto-sans(base); + @include m.font-noto-sans(); height: 80px; margin-top: 8px; @@ -81,7 +81,7 @@ } &__price { - @include m.font-noto-sans(base); + @include m.font-noto-sans(); margin-bottom: 18px; color: var(--text-primary); @@ -111,6 +111,24 @@ text-align: center; } + &__features { + display: flex; + flex-direction: column; + row-gap: 8px; + + b { + color: var(--text-primary); + } + + > p:empty { + display: none; + } + + > p { + margin: 0; + } + } + ul { position: relative; margin: 40px 0 44px 10px; @@ -126,12 +144,17 @@ padding-left: 12px; } + &:has(.with-icon) { + margin-left: 0; + padding-left: 0; + } + li { @include m.font-poppins(v.$fw-medium); @include m.font-scale(base); position: relative; - margin-bottom: 10px; + margin-bottom: 8px; padding-left: 26px; color: var(--text-primary); @@ -150,6 +173,23 @@ display: contents; } } + + li.with-icon { + margin-bottom: 10px; + padding-left: 36px; + + &::before { + content: none; + } + + svg { + position: absolute; + top: -2px; + left: 0; + width: 24px; + height: 24px; + } + } } .btn { @@ -165,3 +205,8 @@ } } } + +.price-description-tooltip .ant-tooltip-inner { + border-radius: 8px; + padding: 16px; +} diff --git a/src/components/PricingCard/PricingCard.tsx b/src/components/PricingCard/PricingCard.tsx index 8de2790b1..f73d533c7 100644 --- a/src/components/PricingCard/PricingCard.tsx +++ b/src/components/PricingCard/PricingCard.tsx @@ -1,6 +1,8 @@ import React, { FC } from 'react'; import classNames from 'classnames'; +import { Tooltip } from 'antd'; import { renderRichText } from 'gatsby-source-contentful/rich-text'; +import { BLOCKS } from '@contentful/rich-text-types'; import { Link } from '@app/components/Link'; import { createBemBlockBuilder, @@ -11,6 +13,11 @@ import { OfferingPlanDto, } from '@app/utils'; import ArrowIcon from '@app/svg/arrow.inline.svg'; +import DealIcon from '@app/svg/deal.inline.svg'; +import ToolIcon from '@app/svg/tool.inline.svg'; +import StarIcon from '@app/svg/star.inline.svg'; +import HeadphonesIcon from '@app/svg/headphones.inline.svg'; +import InfoIcon from '@app/svg/infoIcon.inline.svg'; import './PricingCard.scss'; @@ -21,10 +28,27 @@ interface PricingCardProps { dataGtm?: string; isDiamond?: boolean; isFullWidth?: boolean; + isTotalYearPriceShown?: boolean; } +const knownIcons = { + deal: , + tool: , + star: , + headphones: , +}; + const getBlocksWith = createBemBlockBuilder(['pricing-card']); +const parseFeaturesListItem = (text: string) => { + const iconRegex = /\[icon:(\w+)]/; + const iconMatch = text.match(iconRegex); + const icon = iconMatch ? iconMatch[1] : null; + const content = text.replace(iconRegex, '').trim(); + + return { icon, content }; +}; + export const PricingCard: FC = ({ plan, listItems, @@ -32,12 +56,15 @@ export const PricingCard: FC = ({ isFullWidth, dataGtm, isDiamond = false, + isTotalYearPriceShown = false, }) => { const href = plan.cta.link.url; const priceDescription = plan.price?.[`${planType}Description`]?.replace( '{{currency}}', plan.price.currency, ); + const currency = plan.price?.currency; + const price = plan.price?.[planType] as number; return (
@@ -57,7 +84,32 @@ export const PricingCard: FC = ({ ))} )} - {plan.features && renderRichText(plan.features)} + {plan.features && ( +
+ {renderRichText(plan.features, { + renderNode: { + // eslint-disable-next-line react/no-multi-comp + [BLOCKS.LIST_ITEM]: (node, children) => { + const { icon, content } = parseFeaturesListItem( + children?.[0]?.props?.children?.[0], + ); + const iconElement = knownIcons[icon as string]; + + if (Array.isArray(children) && iconElement) { + return ( +
  • + {iconElement} + {content} +
  • + ); + } + + return
  • {children}
  • ; + }, + }, + })} +
    + )}
    @@ -66,13 +118,32 @@ export const PricingCard: FC = ({ ) : ( <> - {plan.price?.currency} {formatNumberWithCommas(plan.price?.[planType] as number)} + {currency} + {formatNumberWithCommas(price)} {isDiamond && '+'} - / {plan.price?.period} + per {plan.price?.period} )} - {
    {priceDescription}
    } + { +
    + {priceDescription} + {isTotalYearPriceShown && ( + <> + {' '} + + + + + )} +
    + }
    > = ({
    {offerType}
    {description}
    - - - + })} + > + + + )} ); }; diff --git a/src/containers/AcceleratorsPage/D4jPage/D4jPage.tsx b/src/containers/AcceleratorsPage/D4jPage/D4jPage.tsx index 78e6d60b4..21c2c8968 100644 --- a/src/containers/AcceleratorsPage/D4jPage/D4jPage.tsx +++ b/src/containers/AcceleratorsPage/D4jPage/D4jPage.tsx @@ -39,7 +39,7 @@ export const D4jPage: FC = () => { faqData={FAQ_DATA} contactUsLink="/contact-us/d4j" utilizationDescription="Our team will provide services in support of Client's use of Drill4J plugin. Such services will vary based on Client's needs. The table below describes the different support services we customarily provide" - faqLink="https://drill4j.github.io/docs/faq" + faqLink="https://drill4j.github.io/docs/what-is-drill4j" isScaleShifted /> ); diff --git a/src/containers/CommunityPage/CodeOfConduct/CodeOfConduct.tsx b/src/containers/CommunityPage/CodeOfConduct/CodeOfConduct.tsx deleted file mode 100644 index fcb84e5ad..000000000 --- a/src/containers/CommunityPage/CodeOfConduct/CodeOfConduct.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React, { FC } from 'react'; -import classNames from 'classnames'; -import { Link } from '@app/components/Link'; -import { createBemBlockBuilder } from '@app/utils'; - -import codeOfConductSvg from '../icons/code-of-conduct.svg'; - -import './CodeOfConduct.scss'; - -const getBlocksWith = createBemBlockBuilder(['code-of-conduct']); - -export const CodeOfConduct: FC = () => ( -
    - -
    - Code of conduct -

    - Learn about our guidelines for fostering an inclusive and respectful environment within the - ReportPortal community. -

    -
    - - Open on GitHub - -
    -); diff --git a/src/containers/CommunityPage/CodeOfConduct/index.ts b/src/containers/CommunityPage/CodeOfConduct/index.ts deleted file mode 100644 index 2e471f389..000000000 --- a/src/containers/CommunityPage/CodeOfConduct/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './CodeOfConduct'; diff --git a/src/containers/CommunityPage/CommunityPage.tsx b/src/containers/CommunityPage/CommunityPage.tsx index 9268a9498..dcd51c8c3 100644 --- a/src/containers/CommunityPage/CommunityPage.tsx +++ b/src/containers/CommunityPage/CommunityPage.tsx @@ -1,18 +1,19 @@ import React, { FC } from 'react'; import { StartTestingWithReportPortal } from '@app/components/StartTestingWithReportPortal'; import { Link } from '@app/components/Link'; +import { InfoCard } from '@app//components/InfoCard'; import { LinkedCardBlock } from '@app/components/LinkedCardBlock'; import { SubscriptionBanner } from '@app/components/SubscriptionBanner'; import { DOCUMENTATION_URL } from '@app/utils'; import { Hero } from './Hero'; -import { CodeOfConduct } from './CodeOfConduct'; import { JoinOurCommunity } from './JoinOurCommunity'; import { Sponsors } from './Sponsors'; import { StyleMeets } from './StyleMeets'; import { YouTube } from './YouTube'; import { LatestFromOurBlog } from '../LandingPage/LatestFromOurBlog'; import { GITHUB_CONTRIBUTION_CARDS, DOCUMENTATION_CARDS } from './constants'; +import codeOfConductSvg from './icons/code-of-conduct.svg'; import './CommunityPage.scss'; @@ -25,7 +26,16 @@ export const CommunityPage: FC = () => ( of test automation" cardsInfo={GITHUB_CONTRIBUTION_CARDS} > - + diff --git a/src/containers/ContactUsPage/ContactUsDetails/ContactUsDetails.tsx b/src/containers/ContactUsPage/ContactUsDetails/ContactUsDetails.tsx index 415335628..b71436c3f 100644 --- a/src/containers/ContactUsPage/ContactUsDetails/ContactUsDetails.tsx +++ b/src/containers/ContactUsPage/ContactUsDetails/ContactUsDetails.tsx @@ -1,5 +1,4 @@ import React, { FC, Fragment } from 'react'; -import capitalize from 'lodash/capitalize'; import { renderRichText } from 'gatsby-source-contentful/rich-text'; import { ContactUsConfig, @@ -20,10 +19,7 @@ export const ContactUsDetails: FC<

    Price: {price.currency} - {formatNumberWithCommas(price[planType] as number)}/{price.period} - - - Billing period: {capitalize(planType)} + {formatNumberWithCommas(price[planType] as number)} per {price.period} (billed {planType})

    ) : null; diff --git a/src/containers/ContactUsPage/ContactUsForm/ContactUsForm.tsx b/src/containers/ContactUsPage/ContactUsForm/ContactUsForm.tsx index 0c485487d..f6c97fb66 100644 --- a/src/containers/ContactUsPage/ContactUsForm/ContactUsForm.tsx +++ b/src/containers/ContactUsPage/ContactUsForm/ContactUsForm.tsx @@ -67,7 +67,7 @@ export const ContactUsForm = ({ title, options, isDiscussFieldShown }) => { return (
    -
    + { - const requestsWrapper = useRef(null); + const requestsWrapper = useRef(null); const scrollDirection = useScrollDirection({ isMenuOpen: false }); useEffect(() => { diff --git a/src/containers/SassPage/PricingCards/PricingCards.scss b/src/containers/SassPage/PricingCards/PricingCards.scss index fe640498f..d272f3ff1 100644 --- a/src/containers/SassPage/PricingCards/PricingCards.scss +++ b/src/containers/SassPage/PricingCards/PricingCards.scss @@ -6,7 +6,8 @@ flex-flow: column wrap; place-content: center center; gap: 40px 20px; - margin: -244px 16px 0; + margin: 40px 16px 0; + padding-bottom: 80px; @include m.breakpoint(v.$desktop-sm) { flex-flow: row nowrap; diff --git a/src/containers/SassPage/SaasPage.tsx b/src/containers/SassPage/SaasPage.tsx index ba118d2a0..8d021ec4d 100644 --- a/src/containers/SassPage/SaasPage.tsx +++ b/src/containers/SassPage/SaasPage.tsx @@ -1,6 +1,5 @@ import React, { FC } from 'react'; import { graphql, useStaticQuery } from 'gatsby'; -import classNames from 'classnames'; import { TrustedOrganizations } from '@app/components/TrustedOrganizations'; import { ComparePlans } from '@app/components/ComparePlans'; import { Faq } from '@app/components/Faq'; @@ -64,11 +63,11 @@ export const SaasPage: FC = () => { isAnimationEnabled={isAnimationEnabled} /> -
    - -
    -
    - +
    +
    + + +
    diff --git a/src/fragments.ts b/src/fragments.ts index 27409f16a..b5706a733 100644 --- a/src/fragments.ts +++ b/src/fragments.ts @@ -29,7 +29,6 @@ export const ComparePlanItemFragment = graphql` export const ComparePlanFragment = graphql` fragment ComparePlanFields on ContentfulComparePlan { columns - mobileColumns sections { title items { diff --git a/src/hooks/usePricingHeroProps.tsx b/src/hooks/usePricingHeroProps.tsx index 0d885cb3e..0a6bbccbd 100644 --- a/src/hooks/usePricingHeroProps.tsx +++ b/src/hooks/usePricingHeroProps.tsx @@ -14,7 +14,7 @@ const pricingButtons = [ linkTo: '/pricing/saas', }, { - text: 'On-Premises', + text: 'On-Prem Services', icon: , linkTo: '/pricing/on-premises', }, diff --git a/src/styles/global.scss b/src/styles/global.scss index bb8be8ca8..34360a989 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -98,10 +98,6 @@ input:-webkit-autofill { padding-right: 24px; padding-left: 24px; } - - @include m.breakpoint(v.$desktop-sm) { - width: calc(var(--size-max-width) + 48px); - } } .temporary-hide { diff --git a/src/svg/deal.inline.svg b/src/svg/deal.inline.svg new file mode 100644 index 000000000..ee1ea9527 --- /dev/null +++ b/src/svg/deal.inline.svg @@ -0,0 +1,2 @@ + + diff --git a/src/svg/headphones.inline.svg b/src/svg/headphones.inline.svg new file mode 100644 index 000000000..83d065da1 --- /dev/null +++ b/src/svg/headphones.inline.svg @@ -0,0 +1 @@ + diff --git a/src/svg/infoIcon.inline.svg b/src/svg/infoIcon.inline.svg index 7b46cf204..6b4179518 100644 --- a/src/svg/infoIcon.inline.svg +++ b/src/svg/infoIcon.inline.svg @@ -1,3 +1,5 @@ - - + + + + diff --git a/src/svg/star.inline.svg b/src/svg/star.inline.svg new file mode 100644 index 000000000..6ba37d15a --- /dev/null +++ b/src/svg/star.inline.svg @@ -0,0 +1 @@ + diff --git a/src/svg/tool.inline.svg b/src/svg/tool.inline.svg new file mode 100644 index 000000000..1f849a61c --- /dev/null +++ b/src/svg/tool.inline.svg @@ -0,0 +1 @@ + diff --git a/src/utils/contactUsConfig.ts b/src/utils/contactUsConfig.ts index 990b79111..d145b2499 100644 --- a/src/utils/contactUsConfig.ts +++ b/src/utils/contactUsConfig.ts @@ -1,8 +1,10 @@ +import kebabCase from 'lodash/kebabCase'; + import { ContactUsBaseConfig, PlanType } from './types'; const SALESFORCE_SOURCE_NAME = 'ReportPortalSource'; const LEAD_SOURCE = 'lead_source'; -const packageNumbers = [25, 60, 160]; +const onPremisesAndAcceleratorsPlans = ['Lite', 'Service Pro', 'Advanced']; const availableOptions: PlanType[] = ['quarterly', 'yearly']; const createConfig = ({ @@ -35,115 +37,46 @@ const createConfig = ({ const createStartupPlanBaseConfig = (planType: PlanType) => ({ id: '[SaaS] Startup Plan', - options: [ - { - name: LEAD_SOURCE, - value: 'RP SaaS', - }, - ], + options: [{ name: LEAD_SOURCE, value: 'RP SaaS' }], planType, }); const createBusinessPlanBaseConfig = (planType: PlanType) => ({ id: '[SaaS] Business Plan', - options: [ - { - name: LEAD_SOURCE, - value: 'RP SaaS', - }, - ], + options: [{ name: LEAD_SOURCE, value: 'RP SaaS' }], planType, }); const createEnterprisePlanBaseConfig = () => ({ id: '[SaaS] Enterprise Plan', - options: [ - { - name: LEAD_SOURCE, - value: 'RP SaaS', - }, - ], -}); - -const createPackage25BaseConfig = (planType: PlanType) => ({ - id: '[On-Premises] Package 25', - options: [ - { - name: LEAD_SOURCE, - value: 'RP Service', - }, - ], - planType, -}); - -const createPackage60BaseConfig = (planType: PlanType) => ({ - id: '[On-Premises] Package 60', - options: [ - { - name: LEAD_SOURCE, - value: 'RP Service', - }, - ], - planType, -}); - -const createPackage160BaseConfig = (planType: PlanType) => ({ - id: '[On-Premises] Package 160', - options: [ - { - name: LEAD_SOURCE, - value: 'RP Service', - }, - ], - planType, + options: [{ name: LEAD_SOURCE, value: 'RP SaaS' }], }); const createContactUsConfig = ({ salesforceSourceName, leadSource, url }) => ({ id: 'Contact us', url, options: [ - { - name: SALESFORCE_SOURCE_NAME, - value: `Landing page / ${salesforceSourceName}`, - }, - { - name: LEAD_SOURCE, - value: leadSource, - }, + { name: SALESFORCE_SOURCE_NAME, value: `Landing page / ${salesforceSourceName}` }, + { name: LEAD_SOURCE, value: leadSource }, ], isDiscussFieldShown: false, }); const QaSpaceBaseConfig = { id: 'QaSpace', - options: [ - { - name: LEAD_SOURCE, - value: 'RP QASP', - }, - ], + options: [{ name: LEAD_SOURCE, value: 'RP QASP' }], isDiscussFieldShown: false, areCertificatesShown: false, }; const Drill4JBaseConfig = { id: 'Drill4J', - options: [ - { - name: LEAD_SOURCE, - value: 'RP D4J', - }, - ], + options: [{ name: LEAD_SOURCE, value: 'RP D4J' }], isDiscussFieldShown: false, areCertificatesShown: false, }; const HealeniumBaseConfig = { id: 'Healenium', - options: [ - { - name: LEAD_SOURCE, - value: 'RP HLM', - }, - ], + options: [{ name: LEAD_SOURCE, value: 'RP HLM' }], isDiscussFieldShown: false, areCertificatesShown: false, }; @@ -162,24 +95,6 @@ export const contactUsBaseConfigs: ContactUsBaseConfig[] = [ source: 'Landing page / SaaS / Request "Business Plan"', planType, }), - createConfig({ - baseConfig: createPackage25BaseConfig(planType), - url: '/contact-us/on-premises/package-25', - source: 'Landing page / On-Premises / Request Support "Package 25"', - planType, - }), - createConfig({ - baseConfig: createPackage60BaseConfig(planType), - url: '/contact-us/on-premises/package-60', - source: 'Landing page / On-Premises / Request Support "Package 60"', - planType, - }), - createConfig({ - baseConfig: createPackage160BaseConfig(planType), - url: '/contact-us/on-premises/package-160', - source: 'Landing page / On-Premises / Request Support "Package 160"', - planType, - }), createConfig({ baseConfig: createEnterprisePlanBaseConfig(), url: '/contact-us/saas/enterprise-plan', @@ -203,77 +118,76 @@ export const contactUsBaseConfigs: ContactUsBaseConfig[] = [ source: 'Landing page / SaaS / Compare Plan / Request "Enterprise Plan"', }), createConfig({ - baseConfig: createPackage25BaseConfig('yearly'), - url: '/contact-us/on-premises/compare/package-25', - source: 'Landing page / On-Premises / Compare Plan / Request Support "Package 25"', - }), - createConfig({ - baseConfig: createPackage60BaseConfig('yearly'), - url: '/contact-us/on-premises/compare/package-60', - source: 'Landing page / On-Premises / Compare Plan / Request Support "Package 60"', + baseConfig: QaSpaceBaseConfig, + url: '/contact-us/qasp', + source: 'Landing page / RP QASP', }), createConfig({ - baseConfig: createPackage160BaseConfig('yearly'), - url: '/contact-us/on-premises/compare/package-160', - source: 'Landing page / On-Premises / Compare Plan / Request Support "Package 160"', + baseConfig: Drill4JBaseConfig, + url: '/contact-us/d4j', + source: 'Landing page / RP D4J', }), createConfig({ - baseConfig: QaSpaceBaseConfig, - url: '/contact-us/qasp', - source: 'Landing page / RP QASP', + baseConfig: HealeniumBaseConfig, + url: '/contact-us/hlm', + source: 'Landing page / RP HLM', }), - ...packageNumbers.map(packageNumber => + ...onPremisesAndAcceleratorsPlans.flatMap(plan => [ + { + id: `[On-Premises] ${plan}`, + options: [ + { name: LEAD_SOURCE, value: 'RP Service' }, + { + name: SALESFORCE_SOURCE_NAME, + value: `Landing page / On-Premises / Request Support "${plan} Plan"`, + }, + ], + url: `/contact-us/on-premises/${kebabCase(plan)}`, + planType: 'yearly', + }, + { + id: `[On-Premises] ${plan}`, + options: [ + { name: LEAD_SOURCE, value: 'RP Service' }, + { + name: SALESFORCE_SOURCE_NAME, + value: `Landing page / On-Premises / Compare Plan / Request Support "${plan} Plan"`, + }, + ], + url: `/contact-us/on-premises/compare/${kebabCase(plan)}`, + planType: 'yearly', + }, createConfig({ baseConfig: QaSpaceBaseConfig, - url: `/contact-us/qasp/package-${packageNumber}`, - source: `Landing page / QASP / Request Support "Package ${packageNumber}"`, + url: `/contact-us/qasp/${kebabCase(plan)}`, + source: `Landing page / QASP / Request Support "${plan} Plan"`, }), - ), - ...packageNumbers.map(packageNumber => createConfig({ baseConfig: QaSpaceBaseConfig, - url: `/contact-us/qasp/compare/package-${packageNumber}`, - source: `Landing page / QASP / Compare Plan / Request Support "Package ${packageNumber}"`, + url: `/contact-us/qasp/compare/${kebabCase(plan)}`, + source: `Landing page / QASP / Compare Plan / Request Support "${plan} Plan"`, }), - ), - createConfig({ - baseConfig: Drill4JBaseConfig, - url: '/contact-us/d4j', - source: 'Landing page / RP D4J', - }), - ...packageNumbers.map(packageNumber => createConfig({ baseConfig: Drill4JBaseConfig, - url: `/contact-us/d4j/package-${packageNumber}`, - source: `Landing page / D4J / Request Support "Package ${packageNumber}"`, + url: `/contact-us/d4j/${kebabCase(plan)}`, + source: `Landing page / D4J / Request Support "${plan} Plan"`, }), - ), - ...packageNumbers.map(packageNumber => createConfig({ baseConfig: Drill4JBaseConfig, - url: `/contact-us/d4j/compare/package-${packageNumber}`, - source: `Landing page / D4J / Compare Plan / Request Support "Package ${packageNumber}"`, + url: `/contact-us/d4j/compare/${kebabCase(plan)}`, + source: `Landing page / D4J / Compare Plan / Request Support "${plan} Plan"`, }), - ), - createConfig({ - baseConfig: HealeniumBaseConfig, - url: '/contact-us/hlm', - source: 'Landing page / RP HLM', - }), - ...packageNumbers.map(packageNumber => createConfig({ baseConfig: HealeniumBaseConfig, - url: `/contact-us/hlm/package-${packageNumber}`, - source: `Landing page / HLM / Request Support "Package ${packageNumber}"`, + url: `/contact-us/hlm/${kebabCase(plan)}`, + source: `Landing page / HLM / Request Support "${plan} Plan"`, }), - ), - ...packageNumbers.map(packageNumber => createConfig({ baseConfig: HealeniumBaseConfig, - url: `/contact-us/hlm/compare/package-${packageNumber}`, - source: `Landing page / HLM / Compare Plan / Request Support "Package ${packageNumber}"`, + url: `/contact-us/hlm/compare/${kebabCase(plan)}`, + source: `Landing page / HLM / Compare Plan / Request Support "${plan} Plan"`, }), - ), + ]), { id: 'TaaS', url: '/contact-us/taas', diff --git a/src/utils/formatOfferingPlans.ts b/src/utils/formatOfferingPlans.ts index 893cb404e..de53642fa 100644 --- a/src/utils/formatOfferingPlans.ts +++ b/src/utils/formatOfferingPlans.ts @@ -12,7 +12,6 @@ export const formatOfferingPlans = (dto: OfferingPlansQuery) => { comparePlans: { ...comparePlans, columns: JSON.parse(comparePlans.columns) as string[], - mobileColumns: JSON.parse(comparePlans.mobileColumns) as string[], sections: comparePlans.sections.map(section => ({ ...section, items: section.items.map(item => ({ diff --git a/src/utils/types.ts b/src/utils/types.ts index 09d3a5ba8..189c9c6e5 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -102,7 +102,6 @@ export interface ComparePlansDto { note: string; ctas: Required[]; columns: string; - mobileColumns: string; sections: { title: string; items: ComparePlansItemDto[] }[]; }