diff --git a/.github/workflows/deploy-dev-base.yml b/.github/workflows/deploy-dev-base.yml deleted file mode 100644 index 0f4bdec32..000000000 --- a/.github/workflows/deploy-dev-base.yml +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright 2024 EPAM Systems -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: Deploy to dev base (AWS S3) -env: - AWS_S3_BUCKET_NAME : rpp-landing - AWS_REGION_NAME : eu-central-1 - BUILD_DIR : public/ - CONTACT_US_URL: https://testio--partial.sandbox.my.salesforce-sites.com/leadcapture/services/apexrest/leadservice - DOCUMENTATION_URL: //reportportal.io/docs - GTM_ID: GTM-MK7ZHTL - CLOUDFRONT_ID: EILUB1IE9EON0 - CONTENTFUL_ENV_ID: master - GATSBY_MAILCHIMP_LIST_ID: ca6d0eec5b - -on: - workflow_call: - secrets: - CONTENTFUL_ACCESS_TOKEN: - required: true - AWS_ACCESS_KEY_ID: - required: true - AWS_SECRET_ACCESS_KEY: - required: true - CONTENTFUL_SPACE_ID_DEV: - required: true - inputs: - CONTENTFUL_HOST: - required: false - type: string - default: cdn.contentful.com - -jobs: - empty-s3-bucket: - runs-on: ubuntu-latest - steps: - - name: Set AWS credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ${{ env.AWS_REGION_NAME }} - - - name: Empty AWS S3 bucket - run: aws s3 rm s3://${{ env.AWS_S3_BUCKET_NAME }} --recursive - - deploy: - runs-on: ubuntu-latest - needs: [empty-s3-bucket] - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 18 - - - name: Install of node dependencies - run: npm install - - - name: create env file - run: | - touch .env.production - echo CONTENTFUL_ACCESS_TOKEN=${{ secrets.CONTENTFUL_ACCESS_TOKEN }} >> .env.production - echo CONTENTFUL_SPACE_ID=${{ secrets.CONTENTFUL_SPACE_ID_DEV }} >> .env.production - echo CONTENTFUL_ENV_ID=${{ env.CONTENTFUL_ENV_ID }} >> .env.production - echo CONTENTFUL_HOST=${{ inputs.CONTENTFUL_HOST }} >> .env.production - echo GTM_ID=${{ env.GTM_ID }} >> .env.production - echo CONTACT_US_URL=${{ env.CONTACT_US_URL }} >> .env.production - echo DOCUMENTATION_URL=${{ env.DOCUMENTATION_URL }} >> .env.production - echo GATSBY_MAILCHIMP_LIST_ID=${{ env.GATSBY_MAILCHIMP_LIST_ID }} >> .env.production - - - name: Build the source code - run: npm run build - - - name: Set AWS credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ${{ env.AWS_REGION_NAME }} - - - name: Deploy to AWS S3 - run: aws s3 sync ./${{ env.BUILD_DIR }} s3://${{ env.AWS_S3_BUCKET_NAME }} - - - name: Clearing CloudFront cache for the entire distribution - run: aws cloudfront create-invalidation --distribution-id ${{ env.CLOUDFRONT_ID }} --paths "/*" diff --git a/.github/workflows/deploy-dev-preview.yml b/.github/workflows/deploy-dev-preview.yml deleted file mode 100644 index f67b273ff..000000000 --- a/.github/workflows/deploy-dev-preview.yml +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2024 EPAM Systems -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: Deploy to dev in Contentful preview mode (AWS S3) - -on: - workflow_dispatch: - -jobs: - use-base: - uses: ./.github/workflows/deploy-dev-base.yml - secrets: - CONTENTFUL_ACCESS_TOKEN: ${{ secrets.CONTENTFUL_PREVIEW_ACCESS_TOKEN_DEV }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - CONTENTFUL_SPACE_ID_DEV: ${{ secrets.CONTENTFUL_SPACE_ID_DEV }} - with: - CONTENTFUL_HOST: preview.contentful.com diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index 8bd513b31..a6e5fd531 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -11,7 +11,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -name: Deploy to dev in Contentful production mode (AWS S3) +name: Deploy to dev (AWS S3) +env: + AWS_S3_BUCKET_NAME : rpp-landing + AWS_REGION_NAME : eu-central-1 + BUILD_DIR : public/ + CONTACT_US_URL: https://testio--partial.sandbox.my.salesforce-sites.com/leadcapture/services/apexrest/leadservice + DOCUMENTATION_URL: //reportportal.io/docs + GTM_ID: GTM-MK7ZHTL + CLOUDFRONT_ID: EILUB1IE9EON0 + GATSBY_MAILCHIMP_LIST_ID: ca6d0eec5b + CONTENTFUL_ENV_ID: master + CONTENTFUL_HOST: cdn.contentful.com on: push: @@ -20,12 +31,97 @@ on: paths-ignore: - README.md workflow_dispatch: + secrets: + CONTENTFUL_ACCESS_TOKEN: + required: true + CONTENTFUL_PREVIEW_ACCESS_TOKEN_DEV: + required: true + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true + CONTENTFUL_SPACE_ID_DEV: + required: true + inputs: + CONTENTFUL_ENV_ID: + description: 'Contentful environment to use' + type: choice + default: master + options: + - master + - dev + CONTENTFUL_PREVIEW: + description: 'Use Contentful preview?' + type: boolean + default: false jobs: - use-base: - uses: ./.github/workflows/deploy-dev-base.yml - secrets: - CONTENTFUL_ACCESS_TOKEN: ${{ secrets.CONTENTFUL_ACCESS_TOKEN_DEV }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - CONTENTFUL_SPACE_ID_DEV: ${{ secrets.CONTENTFUL_SPACE_ID_DEV }} + empty-s3-bucket: + runs-on: ubuntu-latest + steps: + - name: Set AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION_NAME }} + + - name: Empty AWS S3 bucket + run: aws s3 rm s3://${{ env.AWS_S3_BUCKET_NAME }} --recursive + + deploy: + runs-on: ubuntu-latest + needs: [empty-s3-bucket] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 18 + + - name: Install of node dependencies + run: npm install + + - name: create env file + run: | + touch .env.production + + CONTENTFUL_ENV_ID=${{ env.CONTENTFUL_ENV_ID }} + CONTENTFUL_HOST=${{ env.CONTENTFUL_HOST }} + CONTENTFUL_ACCESS_TOKEN=${{ secrets.CONTENTFUL_ACCESS_TOKEN }} + + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + CONTENTFUL_ENV_ID=${{ github.event.inputs.CONTENTFUL_ENV_ID }} + + if [[ "${{ github.event.inputs.CONTENTFUL_PREVIEW }}" == "true" ]]; then + CONTENTFUL_HOST=preview.contentful.com + CONTENTFUL_ACCESS_TOKEN=${{ secrets.CONTENTFUL_PREVIEW_ACCESS_TOKEN_DEV }} + fi + fi + + echo CONTENTFUL_ACCESS_TOKEN=$CONTENTFUL_ACCESS_TOKEN >> .env.production + echo CONTENTFUL_ENV_ID=$CONTENTFUL_ENV_ID >> .env.production + echo CONTENTFUL_HOST=$CONTENTFUL_HOST >> .env.production + echo CONTENTFUL_SPACE_ID=${{ secrets.CONTENTFUL_SPACE_ID_DEV }} >> .env.production + echo GTM_ID=${{ env.GTM_ID }} >> .env.production + echo CONTACT_US_URL=${{ env.CONTACT_US_URL }} >> .env.production + echo DOCUMENTATION_URL=${{ env.DOCUMENTATION_URL }} >> .env.production + echo GATSBY_MAILCHIMP_LIST_ID=${{ env.GATSBY_MAILCHIMP_LIST_ID }} >> .env.production + + - name: Build the source code + run: npm run build + + - name: Set AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION_NAME }} + + - name: Deploy to AWS S3 + run: aws s3 sync ./${{ env.BUILD_DIR }} s3://${{ env.AWS_S3_BUCKET_NAME }} + + - name: Clearing CloudFront cache for the entire distribution + run: aws cloudfront create-invalidation --distribution-id ${{ env.CLOUDFRONT_ID }} --paths "/*" diff --git a/src/components/AnimatedHeader/AnimatedHeader.tsx b/src/components/AnimatedHeader/AnimatedHeader.tsx index 6abd05146..c5abddc4c 100644 --- a/src/components/AnimatedHeader/AnimatedHeader.tsx +++ b/src/components/AnimatedHeader/AnimatedHeader.tsx @@ -2,17 +2,16 @@ import { createElement, FC, PropsWithChildren } from 'react'; import { motion, Transition } from 'framer-motion'; import { useInView } from '@app/hooks/useInView'; import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; -import { easeInOutTransition, opacityScaleAnimationProps } from '@app/utils'; +import { easeInOutTransition, opacityScaleAnimationProps, PropsWithAnimation } from '@app/utils'; interface AnimatedHeaderProps { transition?: Transition; delay?: number; headerLevel?: 1 | 2 | 3 | 4 | 5 | 6; className?: string; - isAnimationEnabled?: boolean; } -export const AnimatedHeader: FC> = ({ +export const AnimatedHeader: FC>> = ({ transition = easeInOutTransition, delay, headerLevel = 2, @@ -29,7 +28,7 @@ export const AnimatedHeader: FC> = ({ return createElement( motion[`h${headerLevel}`], - { className, ref: titleRef, ...getAnimation({ inView: isTitleInView, delay }) }, + { className, ref: titleRef, ...getAnimation({ isInView: isTitleInView, delay }) }, children, ); }; diff --git a/src/components/AnimatedList/AnimatedList.tsx b/src/components/AnimatedList/AnimatedList.tsx index beef06a9b..940fa5301 100644 --- a/src/components/AnimatedList/AnimatedList.tsx +++ b/src/components/AnimatedList/AnimatedList.tsx @@ -74,7 +74,9 @@ export const AnimatedList: FC = ({
{title} - {subtitle} + + {subtitle} +
= ({ className={getBlocksWithList('__item')} key={itemTitle} onClick={() => setIndexAndResetInterval(index)} - {...getCardAnimation({ inView, delay: 0.2 * index })} + {...getCardAnimation({ isInView: inView, delay: 0.2 * index })} > {itemTitle} @@ -97,7 +99,7 @@ export const AnimatedList: FC = ({ = ({ key={image} alt="" {...getImageAnimation({ - inView, + isInView: inView, delay: isFirstImageAnimationPlayed.current ? 0 : 0.6, additionalEffects: { transitionAdditional: { @@ -134,7 +136,7 @@ export const AnimatedList: FC = ({
{children} diff --git a/src/components/AnnouncementBar/AnnouncementBar.tsx b/src/components/AnnouncementBar/AnnouncementBar.tsx index 6d99f056a..cadfb7409 100644 --- a/src/components/AnnouncementBar/AnnouncementBar.tsx +++ b/src/components/AnnouncementBar/AnnouncementBar.tsx @@ -10,6 +10,7 @@ import { } from '@app/utils'; import { CrossIcon } from '@app/components/Layout/Navigation/icons'; import { ArrowLink } from '@app/components/ArrowLink'; +import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; import MerchIcon from './MerchIcon.inline.svg'; @@ -26,6 +27,12 @@ export const AnnouncementBar: FC = () => { const bannerHeight = isTablet ? desktopBannerHeight : mobileBannerHeight; const heightAnimation = { y: bannerHeight * -1, height: 0 }; + const getAnimation = useMotionEnterAnimation({ + hiddenState: heightAnimation, + enterState: { y: 0, height: bannerHeight }, + transition: { duration: 0.5 }, + }); + const onClose = () => { sessionStorage.setItem(ANNOUNCEMENT_CLOSED_KEY, 'true'); setAnnouncementOpen(false); @@ -34,11 +41,9 @@ export const AnnouncementBar: FC = () => { return (
diff --git a/src/components/ArticlePreview/ArticlePreview.tsx b/src/components/ArticlePreview/ArticlePreview.tsx index be0fcc8fe..e4af07e5b 100644 --- a/src/components/ArticlePreview/ArticlePreview.tsx +++ b/src/components/ArticlePreview/ArticlePreview.tsx @@ -2,7 +2,7 @@ import React, { FC } from 'react'; import isEmpty from 'lodash/isEmpty'; import chunk from 'lodash/chunk'; import { useMediaQuery } from 'react-responsive'; -import { BlogPostDto, MEDIA_DESKTOP_SM, MEDIA_TABLET_SM } from '@app/utils'; +import { BlogPostDto, MEDIA_DESKTOP_SM, MEDIA_TABLET_SM, PropsWithAnimation } from '@app/utils'; import { ArticlePreviewRow } from './ArticlePreviewRow'; @@ -10,6 +10,7 @@ import './ArticlePreview.scss'; interface ArticlePreviewProps { posts: BlogPostDto[]; + hasFixedItemsPerRow?: boolean; } const getItemsPerRow = (isDesktop: boolean, isTablet: boolean) => { @@ -24,15 +25,19 @@ const getItemsPerRow = (isDesktop: boolean, isTablet: boolean) => { return 1; }; -export const ArticlePreview: FC = ({ posts }) => { +export const ArticlePreview: FC> = ({ + posts, + hasFixedItemsPerRow = false, + isAnimationEnabled = false, +}) => { const isDesktop = useMediaQuery({ query: MEDIA_DESKTOP_SM }); const isTablet = useMediaQuery({ query: MEDIA_TABLET_SM }); - const rows = chunk(posts, getItemsPerRow(isDesktop, isTablet)); + const rows = chunk(posts, hasFixedItemsPerRow ? 3 : getItemsPerRow(isDesktop, isTablet)); return !isEmpty(posts) ? (
    {rows.map((row, rowIndex) => ( - + ))}
) : null; diff --git a/src/components/ArticlePreview/ArticlePreviewRow/ArticlePreviewRow.tsx b/src/components/ArticlePreview/ArticlePreviewRow/ArticlePreviewRow.tsx index 0f1c94109..3a37d9ab6 100644 --- a/src/components/ArticlePreview/ArticlePreviewRow/ArticlePreviewRow.tsx +++ b/src/components/ArticlePreview/ArticlePreviewRow/ArticlePreviewRow.tsx @@ -5,6 +5,7 @@ import { createBemBlockBuilder, getEaseInOutTransition, opacityScaleAnimationProps, + PropsWithAnimation, } from '@app/utils'; import { useInView } from '@app/hooks/useInView'; import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; @@ -17,19 +18,25 @@ interface ArticlePreviewRowProps { const getBlocksWith = createBemBlockBuilder(['article-preview-list']); -export const ArticlePreviewRow: FC = ({ row }) => { +export const ArticlePreviewRow: FC> = ({ + row, + isAnimationEnabled = false, +}) => { const [rowRef, isInView] = useInView(); - const getAnimation = useMotionEnterAnimation({ - ...opacityScaleAnimationProps, - ...getEaseInOutTransition(0.7), - }); + const getAnimation = useMotionEnterAnimation( + { + ...opacityScaleAnimationProps, + ...getEaseInOutTransition(0.7), + }, + isAnimationEnabled, + ); return ( = ({ className }) => ( +
+ {certificatesIcons.map(Certificate => ( + + ))} +
+); diff --git a/src/components/CertificationCard/Certificates/index.ts b/src/components/CertificationCard/Certificates/index.ts new file mode 100644 index 000000000..62a214a39 --- /dev/null +++ b/src/components/CertificationCard/Certificates/index.ts @@ -0,0 +1 @@ +export * from './Certificates'; diff --git a/src/components/CertificationCard/Certificates/svg/isae3402.inline.svg b/src/components/CertificationCard/Certificates/svg/isae3402.inline.svg new file mode 100644 index 000000000..2eeea766d --- /dev/null +++ b/src/components/CertificationCard/Certificates/svg/isae3402.inline.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/components/CertificationCard/Certificates/svg/iso27001.inline.svg b/src/components/CertificationCard/Certificates/svg/iso27001.inline.svg new file mode 100644 index 000000000..1ff5b3ced --- /dev/null +++ b/src/components/CertificationCard/Certificates/svg/iso27001.inline.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/components/CertificationCard/Certificates/svg/iso9001.inline.svg b/src/components/CertificationCard/Certificates/svg/iso9001.inline.svg new file mode 100644 index 000000000..43c9cf517 --- /dev/null +++ b/src/components/CertificationCard/Certificates/svg/iso9001.inline.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/components/CertificationCard/Certificates/svg/soc2.inline.svg b/src/components/CertificationCard/Certificates/svg/soc2.inline.svg new file mode 100644 index 000000000..ee589ff4c --- /dev/null +++ b/src/components/CertificationCard/Certificates/svg/soc2.inline.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/components/CertificationCard/CertificationCard.tsx b/src/components/CertificationCard/CertificationCard.tsx new file mode 100644 index 000000000..0358e7f7b --- /dev/null +++ b/src/components/CertificationCard/CertificationCard.tsx @@ -0,0 +1,37 @@ +import React, { FC } from 'react'; +import cn from 'classnames'; +import { ArrowLink } from '@app/components/ArrowLink'; +import { createBemBlockBuilder } from '@app/utils'; + +import { Certificates } from './Certificates'; + +import './CertificateCard.scss'; + +const getBlocksWith = createBemBlockBuilder(['certification-card']); + +interface CertificationCardProps { + subtitle?: string; + shouldDisplayLink?: boolean; +} + +export const CertificationCard: FC = ({ + subtitle, + shouldDisplayLink = false, +}) => { + return ( +
+

ReportPortal certifications

+ {subtitle &&
{subtitle}
} +
+ +
+ {shouldDisplayLink && ( + + )} +
+ ); +}; diff --git a/src/components/CertificationCard/index.ts b/src/components/CertificationCard/index.ts new file mode 100644 index 000000000..eca4e375f --- /dev/null +++ b/src/components/CertificationCard/index.ts @@ -0,0 +1 @@ +export * from './CertificationCard'; diff --git a/src/components/HeroSwitching/HeroSwitching.tsx b/src/components/HeroSwitching/HeroSwitching.tsx index 4fb9d393c..0d269ef65 100644 --- a/src/components/HeroSwitching/HeroSwitching.tsx +++ b/src/components/HeroSwitching/HeroSwitching.tsx @@ -5,6 +5,7 @@ import { createBemBlockBuilder, defaultSpringTransition, opacityScaleAnimationProps, + PropsWithAnimation, } from '@app/utils'; import { AnimatedHeader } from '@app/components/AnimatedHeader'; import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; @@ -18,13 +19,12 @@ interface HeroSwitchingProps { switchActiveBtn?: (text: string) => void; subtitle?: string; isHeroInView?: boolean; - isAnimationEnabled?: boolean; children?: ReactNode; } const getBlocksWith = createBemBlockBuilder(['hero-switching']); -export const HeroSwitching: FC = ({ +export const HeroSwitching: FC> = ({ title, subtitle, buttons, @@ -55,7 +55,7 @@ export const HeroSwitching: FC = ({ {subtitle && ( {subtitle} @@ -63,7 +63,7 @@ export const HeroSwitching: FC = ({ {children} diff --git a/src/components/Layout/Footer/Footer.scss b/src/components/Layout/Footer/Footer.scss index a2f32b47f..e2d26d953 100644 --- a/src/components/Layout/Footer/Footer.scss +++ b/src/components/Layout/Footer/Footer.scss @@ -237,4 +237,28 @@ margin-top: 0; } } + + &__certificates { + gap: 16px; + margin-top: 24px; + + @include m.breakpoint(v.$tablet-sm-exact) { + margin-bottom: 4px; + + svg { + width: 72px; + height: 72px; + } + } + + @include m.breakpoint(v.$desktop-sm) { + margin-top: 32px; + margin-bottom: 0; + } + + svg { + width: 64px; + height: 64px; + } + } } diff --git a/src/components/Layout/Footer/Footer.tsx b/src/components/Layout/Footer/Footer.tsx index 2562604d7..1df198876 100644 --- a/src/components/Layout/Footer/Footer.tsx +++ b/src/components/Layout/Footer/Footer.tsx @@ -4,6 +4,7 @@ import { Divider } from 'antd'; import classNames from 'classnames'; import { useInView } from 'framer-motion'; import { Link } from '@app/components/Link'; +import { Certificates } from '@app/components/CertificationCard/Certificates'; import { useFooter } from '@app/hooks/useFooter'; import { createBemBlockBuilder, isNewYearMode } from '@app/utils'; @@ -24,12 +25,15 @@ export const Footer: FC = () => {
+ ); +}; diff --git a/src/components/StatisticList/StatisticList.tsx b/src/components/StatisticList/StatisticList.tsx index c65bb7f9c..46d6d48d6 100644 --- a/src/components/StatisticList/StatisticList.tsx +++ b/src/components/StatisticList/StatisticList.tsx @@ -1,4 +1,7 @@ import React, { FC } from 'react'; +import { motion } from 'framer-motion'; +import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; +import { getEaseInOutTransition, PropsWithAnimation } from '@app/utils'; import './StatisticList.scss'; @@ -8,16 +11,42 @@ interface StatisticListProps { entities: string; achievement?: string; }[]; + isInView?: boolean; } -export const StatisticList: FC = ({ statistics }) => ( -
    - {statistics.map(({ quantity, entities, achievement }) => ( -
  • - {quantity} - {entities} - {achievement && {achievement}} -
  • - ))} -
-); +export const StatisticList: FC> = ({ + statistics, + isInView = true, + isAnimationEnabled = false, +}) => { + const getAnimation = useMotionEnterAnimation( + { + hiddenState: { + opacity: 0, + x: -150, + }, + enterState: { + opacity: 1, + x: 0, + }, + ...getEaseInOutTransition(0.5), + }, + isAnimationEnabled, + ); + + return ( +
    + {statistics.map(({ quantity, entities, achievement }, index) => ( + + {quantity} + {entities} + {achievement && {achievement}} + + ))} +
+ ); +}; diff --git a/src/components/SubscriptionBanner/SubscriptionBanner.tsx b/src/components/SubscriptionBanner/SubscriptionBanner.tsx index 879119f8e..7e9f2aa53 100644 --- a/src/components/SubscriptionBanner/SubscriptionBanner.tsx +++ b/src/components/SubscriptionBanner/SubscriptionBanner.tsx @@ -1,24 +1,44 @@ -import React from 'react'; +import React, { FC } from 'react'; +import { motion } from 'framer-motion'; import { FooterContent } from '@app/components/Layout'; import { SubscriptionForm } from '@app/components/SubscriptionForm'; import { Banner } from '@app/components/Banner'; -import { createBemBlockBuilder } from '@app/utils'; +import { + createBemBlockBuilder, + getEaseInOutTransition, + opacityScaleAnimationProps, + PropsWithAnimation, +} from '@app/utils'; +import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; +import { useInView } from '@app/hooks/useInView'; import './SubscriptionBanner.scss'; const getBlocksWith = createBemBlockBuilder(['subscription-banner']); -export const SubscriptionBanner = () => { +export const SubscriptionBanner: FC = ({ isAnimationEnabled = false }) => { + const [ref, isInView] = useInView(); + + const getAnimation = useMotionEnterAnimation( + { + ...opacityScaleAnimationProps, + ...getEaseInOutTransition(0.7), + }, + isAnimationEnabled, + ); + return ( - -
- - - -
-
+
+ + + + + + + +
); }; diff --git a/src/components/WhyInstanceBlocks/WhyInstanceBlocks.tsx b/src/components/WhyInstanceBlocks/WhyInstanceBlocks.tsx index 0b258cc46..593af3880 100644 --- a/src/components/WhyInstanceBlocks/WhyInstanceBlocks.tsx +++ b/src/components/WhyInstanceBlocks/WhyInstanceBlocks.tsx @@ -51,7 +51,7 @@ export const WhyInstanceBlocks: FC = ({
  • @@ -59,7 +59,7 @@ export const WhyInstanceBlocks: FC = ({ = ({ = ({ visiblePosts, allPosts, loadMorePo return ( <> - +
    = ({ visiblePosts, allPosts, loadMorePo > Product updates, news and technology articles - + {visiblePosts.length < allPosts.length && (
    -
    +
    + {areCertificatesShown && }
    diff --git a/src/containers/FeaturesPage/FeaturesPage.tsx b/src/containers/FeaturesPage/FeaturesPage.tsx index 490245cd7..029cad545 100644 --- a/src/containers/FeaturesPage/FeaturesPage.tsx +++ b/src/containers/FeaturesPage/FeaturesPage.tsx @@ -152,7 +152,7 @@ export const FeaturesPage: FC = () => {
    @@ -175,7 +175,7 @@ export const FeaturesPage: FC = () => { alt="" {...getHeroImageAnimation({ delay: 0.3, - inView: isHeroImageInView, + isInView: isHeroImageInView, })} onLoad={() => { if (activeElement) { diff --git a/src/containers/InstallationPage/InstallationPage.tsx b/src/containers/InstallationPage/InstallationPage.tsx index d1216b3ce..a3606b50f 100644 --- a/src/containers/InstallationPage/InstallationPage.tsx +++ b/src/containers/InstallationPage/InstallationPage.tsx @@ -105,7 +105,7 @@ export const InstallationPage: FC = () => { className={getBlocksWith('__main-content')} ref={contentRef} {...getContentAnimation({ - inView: isContentInView, + isInView: isContentInView, additionalEffects: { hiddenAdditional: { y: 50 }, enterAdditional: { y: 0 }, diff --git a/src/containers/InstallationPage/ScrollIndicator/ScrollIndicator.tsx b/src/containers/InstallationPage/ScrollIndicator/ScrollIndicator.tsx index 6ea451549..d33c68547 100644 --- a/src/containers/InstallationPage/ScrollIndicator/ScrollIndicator.tsx +++ b/src/containers/InstallationPage/ScrollIndicator/ScrollIndicator.tsx @@ -76,7 +76,7 @@ export const ScrollIndicator: FC = ({ sections, isInView } className={getBlocksWith('__box-item-line')} style={{ bottom: `${bottomPosition}px` }} {...getLineAnimation({ - inView: isInView, + isInView, additionalEffects: { hiddenAdditional: { top: 0 }, enterAdditional: { top: topPosition }, diff --git a/src/containers/InstallationPage/ScrollIndicator/ScrollItem/ScrollItem.tsx b/src/containers/InstallationPage/ScrollIndicator/ScrollItem/ScrollItem.tsx index 2d110159f..ce25b39a1 100644 --- a/src/containers/InstallationPage/ScrollIndicator/ScrollItem/ScrollItem.tsx +++ b/src/containers/InstallationPage/ScrollIndicator/ScrollItem/ScrollItem.tsx @@ -30,7 +30,7 @@ export const ScrollItem: FC = ({ section, offset }) => { className={getBlocksWith('__box-item')} ref={ref} {...getAnimation({ - inView: isInView, + isInView, additionalEffects: { hiddenAdditional: { x: -50, scale: 1 }, enterAdditional: { x: 0 }, diff --git a/src/containers/LandingPage/BenefitsForBusiness/constants.ts b/src/containers/LandingPage/BenefitsForBusiness/constants.ts index 8f2c8b0ba..cc3159a56 100644 --- a/src/containers/LandingPage/BenefitsForBusiness/constants.ts +++ b/src/containers/LandingPage/BenefitsForBusiness/constants.ts @@ -1,6 +1,6 @@ import feature6 from '@app/svg/featuresListItem6.svg'; import feature8 from '@app/svg/featuresListItem8.svg'; -import feature9 from '@app/svg/featuresListItem9.svg'; +import feature12 from '@app/svg/featuresListItem12.svg'; import feature10 from '@app/svg/featuresListItem10.svg'; import feature11 from '@app/svg/featuresListItem11.svg'; @@ -12,13 +12,6 @@ export const BENEFITS_FOR_BUSINESS_LIST = [ image: feature8, link: '/features/#quality-gates', }, - { - title: 'Testing status visibility', - description: - 'Get pure visibility in testing status across various environments by means of dashboards and history', - image: feature9, - link: '/features/#visualisation-of-tests', - }, { title: 'Transparency of test failures reason', description: 'Detect the root causes of failures and categorize the failures faster', @@ -39,4 +32,11 @@ export const BENEFITS_FOR_BUSINESS_LIST = [ image: feature6, link: '/features/#visualisation-of-tests', }, + { + title: 'Certified security', + description: + 'ReportPortal provides robust data protection, backed by SOC2 Type II certification, ensuring compliance with the top-tier data security and privacy standards', + image: feature12, + link: 'https://reportportal.io/blog/reportportal-completes-soc-2-type-ii-audit', + }, ]; diff --git a/src/containers/LandingPage/CustomersStatistics/CustomersStatistics.tsx b/src/containers/LandingPage/CustomersStatistics/CustomersStatistics.tsx index f38b3550a..ee5376327 100644 --- a/src/containers/LandingPage/CustomersStatistics/CustomersStatistics.tsx +++ b/src/containers/LandingPage/CustomersStatistics/CustomersStatistics.tsx @@ -1,7 +1,10 @@ import React, { FC } from 'react'; +import { motion } from 'framer-motion'; import { useCustomersStatistics } from '@app/hooks/useCustomersStatistics'; -import { createBemBlockBuilder } from '@app/utils'; +import { useInView } from '@app/hooks/useInView'; import { StatisticList } from '@app/components/StatisticList'; +import { createBemBlockBuilder, getEaseInOutTransition } from '@app/utils'; +import { useMotionEnterAnimation } from '@app/hooks/useMotionEnterAnimation'; import './CustomersStatistics.scss'; @@ -9,14 +12,37 @@ const getBlocksWith = createBemBlockBuilder(['customers-statistics']); export const CustomersStatistics: FC = () => { const { title, subTitle, statistics } = useCustomersStatistics(); + const [ref, isInView] = useInView(); + + const getTitleAnimation = useMotionEnterAnimation({ + hiddenState: { + opacity: 0, + x: -50, + }, + enterState: { + opacity: 1, + x: 0, + }, + ...getEaseInOutTransition(0.7), + }); return ( -
    +
    -

    {title}

    -

    {subTitle}

    - + + {title} + + + {subTitle} + +
    diff --git a/src/containers/LandingPage/HowItWorks/HowItWorks.scss b/src/containers/LandingPage/HowItWorks/HowItWorks.scss index 032c9cda7..ea7a21cc4 100644 --- a/src/containers/LandingPage/HowItWorks/HowItWorks.scss +++ b/src/containers/LandingPage/HowItWorks/HowItWorks.scss @@ -92,8 +92,10 @@ .ant-steps-item-icon, .ant-steps-item-tail::after, .ant-steps-item-content { - opacity: 0; - transform: translateX(-50px) scale(0.5); + @include m.breakpoint(v.$tablet-sm-exact) { + opacity: 0; + transform: translateX(-50px) scale(0.5); + } } .ant-steps-item { @@ -207,9 +209,13 @@ background-position: bottom; background-size: 100% 200%; transition: background 0.3s, opacity 0.5s; - animation: progressAnimationBottom linear 10s forwards 0.6s, - stepsAnimation 0.6s ease-in-out forwards; + animation: progressAnimationBottom linear 10s forwards 0.6s; content: ''; + + @include m.breakpoint(v.$tablet-sm-exact) { + animation: progressAnimationBottom linear 10s forwards 0.6s, + stepsAnimation 0.6s ease-in-out forwards; + } } } } @@ -223,12 +229,16 @@ &--in-view .ant-steps-item-icon, &--in-view .ant-steps-item-content { - animation: stepsAnimation 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.3s forwards; + @include m.breakpoint(v.$tablet-sm-exact) { + animation: stepsAnimation 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.3s forwards; + } } &--in-view::after, &--in-view .ant-steps-item-tail::after { - animation: stepsAnimation 0.5s ease-in-out forwards; + @include m.breakpoint(v.$tablet-sm-exact) { + animation: stepsAnimation 0.5s ease-in-out forwards; + } } } diff --git a/src/containers/LandingPage/HowItWorks/HowItWorks.tsx b/src/containers/LandingPage/HowItWorks/HowItWorks.tsx index 1cffc0d53..c821ca2e2 100644 --- a/src/containers/LandingPage/HowItWorks/HowItWorks.tsx +++ b/src/containers/LandingPage/HowItWorks/HowItWorks.tsx @@ -90,13 +90,13 @@ export const HowItWorks = () => {
    {sections[activeListIndex].title} {sections[activeListIndex].content} @@ -106,7 +106,7 @@ export const HowItWorks = () => {
    {isButtonInView && ( - + Learn how to install diff --git a/src/containers/LandingPage/LandingPage.tsx b/src/containers/LandingPage/LandingPage.tsx index 044d09957..12509eb53 100644 --- a/src/containers/LandingPage/LandingPage.tsx +++ b/src/containers/LandingPage/LandingPage.tsx @@ -19,9 +19,9 @@ export const LandingPage: FC = () => ( - - - - + + + + ); diff --git a/src/containers/LandingPage/LatestFromOurBlog/LatestFromOurBlog.tsx b/src/containers/LandingPage/LatestFromOurBlog/LatestFromOurBlog.tsx index e87386cbf..9aa92eb3a 100644 --- a/src/containers/LandingPage/LatestFromOurBlog/LatestFromOurBlog.tsx +++ b/src/containers/LandingPage/LatestFromOurBlog/LatestFromOurBlog.tsx @@ -2,8 +2,9 @@ import React, { FC } from 'react'; import classNames from 'classnames'; import { ArticlePreview } from '@app/components/ArticlePreview'; import { Link } from '@app/components/Link'; -import { createBemBlockBuilder } from '@app/utils'; +import { createBemBlockBuilder, defaultSpringTransition, PropsWithAnimation } from '@app/utils'; import { useLatestFromOurBlog } from '@app/hooks/useLatestFromOurBlog'; +import { AnimatedHeader } from '@app/components/AnimatedHeader'; import './LatestFromOurBlog.scss'; @@ -13,14 +14,23 @@ interface LatestFromOurBlogProps { isViewAll?: boolean; } -export const LatestFromOurBlog: FC = ({ isViewAll }) => { +export const LatestFromOurBlog: FC> = ({ + isViewAll, + isAnimationEnabled = false, +}) => { const posts = useLatestFromOurBlog(); return (
    -

    Latest from our blog

    + + Latest from our blog +
    - +
    {isViewAll && ( diff --git a/src/containers/LandingPage/Showcase/Showcase.tsx b/src/containers/LandingPage/Showcase/Showcase.tsx index 21011a858..7a08f476e 100644 --- a/src/containers/LandingPage/Showcase/Showcase.tsx +++ b/src/containers/LandingPage/Showcase/Showcase.tsx @@ -60,20 +60,20 @@ export const Showcase: FC = () => {
    AI-powered
    Test Automation Dashboard
    Aggregate and analyze test reports to ascertain release health
    {