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

New pricing page #461

Merged
merged 32 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2dffa64
Starting to limit the connectors to 12 before needing to contact us
travjenkins Aug 14, 2024
7aca99b
Change estuary pricing per connector according to new condition
Aug 14, 2024
834ba7b
Replace max connectors with Need More button on pricing page calculator
Aug 14, 2024
5887e0e
Store new pricing variables
Aug 14, 2024
93d539d
Consider halfSelfServiceConnectorLimit to depend on the selfServiceCo…
Aug 14, 2024
e0e8e85
Add lazy loading to svg imgs from testimonials carousel
Aug 20, 2024
e8e4eea
Remove async and keep defer on the zendesk chat and hubspot scripts
Aug 20, 2024
ffb2f5f
Fix the calculations for pricing per connectors
Aug 20, 2024
8121ccf
Merge branch 'master' of https://github.com/estuary/marketing-site in…
Aug 21, 2024
af693fd
Remove old pricing page components and content
Aug 21, 2024
a001056
factoring in connector count
travjenkins Aug 21, 2024
98c3078
updating cost to handle dest and source
travjenkins Aug 21, 2024
780b5dd
Add princing page section one
Aug 22, 2024
60506a5
Remove unnecessary layout constrained from StaticImage since it alrea…
Aug 22, 2024
c8b0054
Merge branch 'travjenkins/feature/pricing-update' of https://github.c…
Aug 22, 2024
a2743bd
Fix hidden numbers of table of contents when numbers are 10 or more
Aug 22, 2024
c9e437e
Move dashboard register url to global shared file
Aug 23, 2024
97fe0e6
Merge branch 'master' into Brenosalv/feature/431-pricing-page-updates
Brenosalv Aug 23, 2024
5cb2245
Add section two and replace Carousel component with Embla one for a b…
Aug 26, 2024
f8471e8
Merge branch 'master' of https://github.com/estuary/marketing-site in…
Aug 26, 2024
cfd8ac6
Solve conflicts
Aug 26, 2024
4711ec3
Add section three and new pricing calculator
Aug 27, 2024
e8df91f
Add section four
Aug 27, 2024
27e6ece
Merge branch 'master' of https://github.com/estuary/marketing-site in…
Aug 27, 2024
31081f0
Add review fixes for the section two
Aug 28, 2024
8815172
Add review fixes for section three
Aug 28, 2024
d3c178f
Small change to type
travjenkins Aug 28, 2024
0a3cc77
Add suggested tweaks
Aug 28, 2024
c11e0d5
Sharing logic for min and max on GB
travjenkins Aug 28, 2024
dff88f4
Making sure how the calculations are done is handled properly
travjenkins Aug 28, 2024
bb581d8
handle edge cases
travjenkins Aug 28, 2024
26639d8
This needs to check if a number is NOT safe
travjenkins Aug 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"clsx": "^1.2.1",
"dayjs": "^1.11.7",
"dotenv": "^16.0.3",
"embla-carousel": "^8.2.0",
"embla-carousel-react": "^8.2.0",
"gatsby": "^5.13.3",
"gatsby-background-image": "^1.6.0",
"gatsby-image": "^3.11.0",
Expand Down
2 changes: 2 additions & 0 deletions shared.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export const webinarsUrl =
'https://try.estuary.dev/webinar-estuary101-ondemand';

export const dashboardRegisterUrl = 'https://dashboard.estuary.dev/register';

export const estuaryProductFlowVideoUrl =
'https://www.youtube.com/embed/hlCh81ZbBik';

Expand Down
8 changes: 6 additions & 2 deletions src/components/Advantages/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const Container = styled.section<{ $isImageOnTheLeft: boolean }>`
flex-direction: ${(props) =>
props.$isImageOnTheLeft ? 'row-reverse' : 'row'};

@media (max-width: 768px) {
@media (max-width: 1024px) {
flex-direction: column-reverse;
gap: 40px;
}
Expand Down Expand Up @@ -45,7 +45,11 @@ export const LeftColumn = styled.div`
display: flex;
flex-direction: column;
gap: 24px;
max-width: 620px;
max-width: 45%;

@media (max-width: 1024px) {
max-width: 100%;
}
`;

export const Title = styled.h2<Theme>`
Expand Down
48 changes: 48 additions & 0 deletions src/components/Carousel/hooks/useDotButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useCallback, useEffect, useState } from 'react';
import { EmblaCarouselType } from 'embla-carousel';

type UseDotButtonType = {
selectedIndex: number;
scrollSnaps: number[];
onDotButtonClick: (index: number) => void;
};

export const useDotButton = (
emblaApi: EmblaCarouselType | undefined
): UseDotButtonType => {
const [selectedIndex, setSelectedIndex] = useState(0);
const [scrollSnaps, setScrollSnaps] = useState<number[]>([]);

const onDotButtonClick = useCallback(
(index: number) => {
if (!emblaApi) return;
emblaApi.scrollTo(index);
},
[emblaApi]
);

const onInit = useCallback((emblaApiValue: EmblaCarouselType) => {
setScrollSnaps(emblaApiValue.scrollSnapList());
}, []);

const onSelect = useCallback((emblaApiValue: EmblaCarouselType) => {
setSelectedIndex(emblaApiValue.selectedScrollSnap());
}, []);

useEffect(() => {
if (!emblaApi) return;

onInit(emblaApi);
onSelect(emblaApi);
emblaApi
.on('reInit', onInit)
.on('reInit', onSelect)
.on('select', onSelect);
}, [emblaApi, onInit, onSelect]);

return {
selectedIndex,
scrollSnaps,
onDotButtonClick,
};
};
45 changes: 45 additions & 0 deletions src/components/Carousel/hooks/usePrevNextButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useCallback, useEffect, useState } from 'react';
import { EmblaCarouselType } from 'embla-carousel';

type UsePrevNextButtonsType = {
prevBtnDisabled: boolean;
nextBtnDisabled: boolean;
onPrevButtonClick: () => void;
onNextButtonClick: () => void;
};

export const usePrevNextButtons = (
emblaApi: EmblaCarouselType | undefined
): UsePrevNextButtonsType => {
const [prevBtnDisabled, setPrevBtnDisabled] = useState(true);
const [nextBtnDisabled, setNextBtnDisabled] = useState(true);

const onPrevButtonClick = useCallback(() => {
if (!emblaApi) return;
emblaApi.scrollPrev();
}, [emblaApi]);

const onNextButtonClick = useCallback(() => {
if (!emblaApi) return;
emblaApi.scrollNext();
}, [emblaApi]);

const onSelect = useCallback((emblaApiValue: EmblaCarouselType) => {
setPrevBtnDisabled(!emblaApiValue.canScrollPrev());
setNextBtnDisabled(!emblaApiValue.canScrollNext());
}, []);

useEffect(() => {
if (!emblaApi) return;

onSelect(emblaApi);
emblaApi.on('reInit', onSelect).on('select', onSelect);
}, [emblaApi, onSelect]);

return {
prevBtnDisabled,
nextBtnDisabled,
onPrevButtonClick,
onNextButtonClick,
};
};
213 changes: 85 additions & 128 deletions src/components/Carousel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,168 +1,125 @@
import * as React from 'react';
import React from 'react';
import { EmblaOptionsType } from 'embla-carousel';
import useEmblaCarousel from 'embla-carousel-react';
import CircleIcon from '@mui/icons-material/Circle';
import clsx from 'clsx';
import ChevronLeftIcon from '../../svgs/chevron-left.svg';
import ChevronRightIcon from '../../svgs/chevron-right.svg';
import { useDotButton } from './hooks/useDotButton';
import { usePrevNextButtons } from './hooks/usePrevNextButtons';
import {
Arrow,
Container,
Dot,
DotWrapper,
Dots,
Navigation,
Slide,
Viewport,
} from './styles';
container,
threeItemsSlide,
viewport,
slideWrapper,
slide,
arrow,
dots,
dotWrapper,
dot,
oneContentSlide,
navigation,
dotSelected,
} from './styles.module.less';

type CarouselProps = React.HTMLAttributes<HTMLDivElement> & {
children: React.ReactNode;
options?: EmblaOptionsType;
hasArrow?: boolean;
dotColor?: string;
activeDotColor?: string;
arrowColor?: string;
hasFullWidthSlide?: boolean;
hasMultipleItemsSlide?: boolean;
};

const Carousel = ({
children,
options,
hasArrow = false,
dotColor,
activeDotColor,
arrowColor = '#FFFFFF',
hasFullWidthSlide = false,
hasMultipleItemsSlide,
...rest
}: CarouselProps) => {
const [currentSlide, setCurrentSlide] = React.useState(0);
const [isTransitioning, setIsTransitioning] = React.useState(false);
const hasMounted = React.useRef(false);
const slideRefs = React.useRef<(HTMLLIElement | null)[]>([]);
const isDotClick = React.useRef(false);
const [emblaRef, emblaApi] = useEmblaCarousel(options);

const isLeftArrowDisabled = currentSlide === 0;
const isRightArrowDisabled = currentSlide === slideRefs.current.length - 1;
const { selectedIndex, scrollSnaps, onDotButtonClick } =
useDotButton(emblaApi);

const observeSlides = React.useCallback(
(observer: IntersectionObserver) => {
slideRefs.current.forEach((slide) => {
if (slide) {
observer.observe(slide);
}
});
},
[]
);

const createIntersectionObserver = React.useCallback(() => {
const observerCallback = (entries: IntersectionObserverEntry[]) => {
entries.forEach((entry) => {
if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
const index = slideRefs.current.indexOf(
entry.target as HTMLLIElement
);
if (index !== -1) {
setCurrentSlide(index);
}
}
});
};

return new IntersectionObserver(observerCallback, { threshold: 0.5 });
}, []);

React.useEffect(() => {
if (hasMounted.current && slideRefs.current[currentSlide]) {
slideRefs.current[currentSlide]?.scrollIntoView({
behavior: isDotClick.current ? 'instant' : 'smooth',
block: 'nearest',
inline: 'center',
});
isDotClick.current = false;
}
}, [currentSlide]);

React.useEffect(() => {
const observer = createIntersectionObserver();
observeSlides(observer);

hasMounted.current = true;

return () => {
observer.disconnect();
};
}, [createIntersectionObserver, observeSlides]);

const onArrowClick = React.useCallback(
(direction: 'left' | 'right') => () => {
if (!isTransitioning) {
setIsTransitioning(true);
const nextSlide =
direction === 'left' ? currentSlide - 1 : currentSlide + 1;
setCurrentSlide(nextSlide);
setTimeout(() => {
setIsTransitioning(false);
}, 500);
}
},
[currentSlide, isTransitioning]
);

const handleDotClick = React.useCallback(
(index: number) => {
setCurrentSlide(index);
isDotClick.current = true;
},
[isDotClick]
);
const {
prevBtnDisabled,
nextBtnDisabled,
onPrevButtonClick,
onNextButtonClick,
} = usePrevNextButtons(emblaApi);

return (
<Container aria-label={rest['aria-label']} {...rest}>
<Viewport>
{React.Children.map(children, (child, index) => (
<Slide
key={`${rest['aria-label']}-${index}}`}
ref={(el) => {
slideRefs.current[index] = el;
}}
>
{child}
</Slide>
))}
</Viewport>
<Navigation>
<div
className={clsx(
container,
hasMultipleItemsSlide ? threeItemsSlide : null
)}
>
<div className={viewport} ref={emblaRef}>
<div className={slideWrapper}>
{React.Children.map(children, (child, index) => (
<div
className={clsx(
slide,
hasFullWidthSlide ? oneContentSlide : null
)}
key={index}
>
{child}
</div>
))}
</div>
</div>

<div className={navigation}>
{hasArrow ? (
<Arrow
onClick={onArrowClick('left')}
disabled={isLeftArrowDisabled}
<button
onClick={onPrevButtonClick}
disabled={prevBtnDisabled}
aria-label="previous slide"
className={arrow}
>
<ChevronLeftIcon
className="carousel-chevron-icon"
color={arrowColor}
/>
</Arrow>
<ChevronLeftIcon color={arrowColor} />
</button>
) : null}
{React.Children.count(children) > 1 ? (
<Dots>
{React.Children.map(children, (_, index) => (
<DotWrapper
$isActive={currentSlide === index}
<ul className={dots}>
{scrollSnaps.map((_, index) => (
<li
className={clsx(
dotWrapper,
selectedIndex === index ? dotSelected : null
)}
key={`${rest['aria-label']}-dot-${index}}`}
>
<Dot onClick={() => handleDotClick(index)} />
</DotWrapper>
<CircleIcon
className={dot}
onClick={() => onDotButtonClick(index)}
/>
</li>
))}
</Dots>
</ul>
) : null}
{hasArrow ? (
<Arrow
onClick={onArrowClick('right')}
disabled={isRightArrowDisabled}
<button
onClick={onNextButtonClick}
disabled={nextBtnDisabled}
aria-label="next slide"
className={arrow}
>
<ChevronRightIcon
className="carousel-chevron-icon"
color={arrowColor}
/>
</Arrow>
<ChevronRightIcon color={arrowColor} />
</button>
) : null}
</Navigation>
</Container>
</div>
</div>
);
};

Expand Down
Loading
Loading