Initial commit
This commit is contained in:
64
src/templates/PricingPage/Community/Carousel.tsx
Normal file
64
src/templates/PricingPage/Community/Carousel.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { Splide, SplideTrack, SplideSlide } from "@splidejs/react-splide";
|
||||
import Comment from "./Comment";
|
||||
|
||||
type CarouselProps = {
|
||||
items: any;
|
||||
};
|
||||
|
||||
const Carousel = ({ items }: CarouselProps) => {
|
||||
const [activeIndex, setActiveIndex] = useState<number>(0);
|
||||
|
||||
const ref = useRef<any>(null);
|
||||
|
||||
const handleClick = (index: number) => {
|
||||
setActiveIndex(index);
|
||||
ref.current?.go(index);
|
||||
};
|
||||
|
||||
return (
|
||||
<Splide
|
||||
className="splide-visible relative z-2"
|
||||
options={{
|
||||
pagination: false,
|
||||
arrows: false,
|
||||
gap: "1.5rem",
|
||||
}}
|
||||
onMoved={(e, newIndex) => setActiveIndex(newIndex)}
|
||||
hasTrack={false}
|
||||
ref={ref}
|
||||
>
|
||||
<SplideTrack>
|
||||
{items.map((item: any) => (
|
||||
<SplideSlide key={item.id}>
|
||||
<div className="flex h-full">
|
||||
<Comment comment={item} />
|
||||
</div>
|
||||
</SplideSlide>
|
||||
))}
|
||||
</SplideTrack>
|
||||
<div className="flex justify-center mt-8 -mx-2 md:mt-15 lg:hidden">
|
||||
{items.map((item: any, index: number) => (
|
||||
<button
|
||||
className="relative w-6 h-6 mx-2"
|
||||
onClick={() => handleClick(index)}
|
||||
key={item.id}
|
||||
>
|
||||
<span
|
||||
className={`absolute inset-0 bg-conic-gradient rounded-full transition-opacity ${
|
||||
index === activeIndex
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
}`}
|
||||
></span>
|
||||
<span className="absolute inset-0.25 bg-n-8 rounded-full">
|
||||
<span className="absolute inset-2 bg-n-1 rounded-full"></span>
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</Splide>
|
||||
);
|
||||
};
|
||||
|
||||
export default Carousel;
|
||||
28
src/templates/PricingPage/Community/Comment.tsx
Normal file
28
src/templates/PricingPage/Community/Comment.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import Image from "@/components/Image";
|
||||
|
||||
type CommentProps = {
|
||||
comment: any;
|
||||
};
|
||||
|
||||
const Comment = ({ comment }: CommentProps) => (
|
||||
<div className="flex flex-col bg-n-8 border border-n-1/5 rounded-2xl">
|
||||
<div className="quote flex-1 px-5 py-10 md:px-10">{comment.text}</div>
|
||||
<div className="flex items-center px-5 py-6 bg-n-7 rounded-b-[0.9375rem] md:px-10">
|
||||
<div className="mr-5">
|
||||
<h6 className="h6">{comment.name}</h6>
|
||||
<div className="caption text-n-1/25">{comment.role}</div>
|
||||
</div>
|
||||
<div className="ml-auto">
|
||||
<Image
|
||||
className="w-full rounded-full"
|
||||
src={comment.avatarUrl}
|
||||
width={60}
|
||||
height={60}
|
||||
alt={comment.name}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Comment;
|
||||
25
src/templates/PricingPage/Community/Grid.tsx
Normal file
25
src/templates/PricingPage/Community/Grid.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import Masonry, { ResponsiveMasonry } from "react-responsive-masonry";
|
||||
import Comment from "./Comment";
|
||||
|
||||
type GridProps = {
|
||||
items: any;
|
||||
};
|
||||
|
||||
const Grid = ({ items }: GridProps) => {
|
||||
return (
|
||||
<ResponsiveMasonry
|
||||
className="relative z-2"
|
||||
columnsCountBreakPoints={{ 768: 2, 1280: 3 }}
|
||||
>
|
||||
<Masonry gutter="1.5rem">
|
||||
{items.map((item: any) => (
|
||||
<div key={item.id}>
|
||||
<Comment comment={item} />
|
||||
</div>
|
||||
))}
|
||||
</Masonry>
|
||||
</ResponsiveMasonry>
|
||||
);
|
||||
};
|
||||
|
||||
export default Grid;
|
||||
50
src/templates/PricingPage/Community/index.tsx
Normal file
50
src/templates/PricingPage/Community/index.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import dynamic from "next/dynamic";
|
||||
import { useMediaQuery } from "react-responsive";
|
||||
import Section from "@/components/Section";
|
||||
import Heading from "@/components/Heading";
|
||||
import Image from "@/components/Image";
|
||||
const Grid = dynamic(() => import("./Grid"), { ssr: false });
|
||||
const Carousel = dynamic(() => import("./Carousel"), { ssr: false });
|
||||
|
||||
import { community } from "@/mocks/community";
|
||||
|
||||
type CommunityProps = {};
|
||||
|
||||
const Community = ({}: CommunityProps) => {
|
||||
const isTablet = useMediaQuery({
|
||||
query: "(min-width: 768px)",
|
||||
});
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<div className="container">
|
||||
<Heading
|
||||
className="md:text-center"
|
||||
tagClassName="md:justify-center"
|
||||
tag="ready to get started"
|
||||
title="What the community is saying"
|
||||
/>
|
||||
<div className="relative">
|
||||
{isTablet ? (
|
||||
<Grid items={community} />
|
||||
) : (
|
||||
<Carousel items={community} />
|
||||
)}
|
||||
<div className="absolute top-[18.25rem] -left-[30.375rem] w-[56.625rem] opacity-60 mix-blend-color-dodge pointer-events-none">
|
||||
<div className="absolute top-1/2 left-1/2 w-[58.85rem] h-[58.85rem] -translate-x-3/4 -translate-y-1/2">
|
||||
<Image
|
||||
className="w-full"
|
||||
src="/images/gradient.png"
|
||||
width={942}
|
||||
height={942}
|
||||
alt="Gradient"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Community;
|
||||
101
src/templates/PricingPage/Comparison/index.tsx
Normal file
101
src/templates/PricingPage/Comparison/index.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import Tippy from "@tippyjs/react";
|
||||
import Heading from "@/components/Heading";
|
||||
import Image from "@/components/Image";
|
||||
import Section from "@/components/Section";
|
||||
|
||||
import { comparison } from "@/mocks/comparison";
|
||||
|
||||
type ComparisonProps = {};
|
||||
|
||||
const Comparison = ({}: ComparisonProps) => {
|
||||
const check = (value: any, enterprise?: boolean) =>
|
||||
typeof value === "boolean" ? (
|
||||
value === true ? (
|
||||
<Image
|
||||
src={
|
||||
enterprise
|
||||
? "/images/check-yellow.svg"
|
||||
: "/images/check.svg"
|
||||
}
|
||||
width={24}
|
||||
height={24}
|
||||
alt="Check"
|
||||
/>
|
||||
) : null
|
||||
) : (
|
||||
value
|
||||
);
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<div className="container">
|
||||
<Heading
|
||||
className="md:text-center"
|
||||
title="Compare plans & features"
|
||||
/>
|
||||
<div className="-mx-5 px-5 overflow-auto">
|
||||
<table className="table-fixed w-full min-w-[32rem]">
|
||||
<tbody>
|
||||
<tr className="h6">
|
||||
<td className="w-[35%] py-4 pr-10">Features</td>
|
||||
<td className="p-4 text-center text-color-2">
|
||||
Basic
|
||||
</td>
|
||||
<td className="p-4 text-center text-color-1">
|
||||
Premium
|
||||
</td>
|
||||
<td className="p-4 text-center text-color-3">
|
||||
Enterprise
|
||||
</td>
|
||||
</tr>
|
||||
{comparison.map((item) => (
|
||||
<tr className="body-2" key={item.id}>
|
||||
<td className="w-[35%] h-[4.75rem] py-2.5 pr-2.5 border-t border-n-1/5">
|
||||
<div className="flex items-center">
|
||||
{item.title}
|
||||
<Tippy
|
||||
className="p-2.5 bg-n-1 text-n-8 rounded-xl"
|
||||
content="Provide dedicated servers for enterprises to ensure maximum security, performance, and uptime."
|
||||
placement="right"
|
||||
animation="shift-toward"
|
||||
>
|
||||
<div className="flex-shrink-0 ml-3 opacity-30 transition-opacity hover:opacity-100">
|
||||
<Image
|
||||
src="/images/icons/help-circle.svg"
|
||||
width={24}
|
||||
height={24}
|
||||
alt="Help"
|
||||
/>
|
||||
</div>
|
||||
</Tippy>
|
||||
</div>
|
||||
</td>
|
||||
<td className="h-[4.75rem] p-2.5 border-t border-n-1/5 text-center">
|
||||
{check(
|
||||
item.pricing[0],
|
||||
item.enterprise
|
||||
)}
|
||||
</td>
|
||||
<td className="h-[4.75rem] p-2.5 border-t border-n-1/5 text-center">
|
||||
{check(
|
||||
item.pricing[1],
|
||||
item.enterprise
|
||||
)}
|
||||
</td>
|
||||
<td className="h-[4.75rem] p-2.5 border-t border-n-1/5 text-center">
|
||||
{check(
|
||||
item.pricing[2],
|
||||
item.enterprise
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Comparison;
|
||||
78
src/templates/PricingPage/Faq/index.tsx
Normal file
78
src/templates/PricingPage/Faq/index.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import { useState } from "react";
|
||||
import Section from "@/components/Section";
|
||||
import Heading from "@/components/Heading";
|
||||
|
||||
import { faq } from "@/mocks/faq";
|
||||
|
||||
type FaqProps = {};
|
||||
|
||||
const Faq = ({}: FaqProps) => {
|
||||
const [activeId, setActiveId] = useState<string | null>(faq[0].id);
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<div className="container lg:flex">
|
||||
<Heading
|
||||
className="lg:min-w-[22.75rem] lg:mr-12 lg:pt-8 xl:min-w-[32.75rem]"
|
||||
textAlignClassName="md:text-center lg:text-left"
|
||||
title="Frequently asked questions"
|
||||
text={
|
||||
<>
|
||||
Haven’t found what you’re looking for?{" "}
|
||||
<a
|
||||
className="text-n-1 hover:text-color-2"
|
||||
href="mailto:info@ui8.net"
|
||||
>
|
||||
Contact us
|
||||
</a>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<div className="-mt-8 lg:mt-0">
|
||||
{faq.map((item) => (
|
||||
<div
|
||||
className="py-8 border-b border-n-1/5"
|
||||
key={item.id}
|
||||
>
|
||||
<div
|
||||
className="flex items-start justify-between cursor-pointer"
|
||||
onClick={() =>
|
||||
setActiveId(
|
||||
activeId === item.id ? null : item.id
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="text-[1.25rem] leading-8">
|
||||
{item.title}
|
||||
</div>
|
||||
<div className="relative w-6 h-6 mt-1 ml-10">
|
||||
<div className="absolute top-[0.6875rem] left-1 w-4 h-0.5 bg-n-1 rounded-sm"></div>
|
||||
<div
|
||||
className={`absolute top-[0.6875rem] left-1 w-4 h-0.5 bg-n-1 rounded-sm transition-transform ${
|
||||
item.id === activeId
|
||||
? ""
|
||||
: "rotate-90"
|
||||
}`}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`grid grid-rows-[0fr] transition-all ${
|
||||
item.id === activeId
|
||||
? "grid-rows-[1fr]"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<div className="body-2 text-n-3 overflow-hidden">
|
||||
<div className="pt-6">{item.text}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Faq;
|
||||
50
src/templates/PricingPage/Pricing/index.tsx
Normal file
50
src/templates/PricingPage/Pricing/index.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import Section from "@/components/Section";
|
||||
import Heading from "@/components/Heading";
|
||||
import PricingList from "@/components/PricingList";
|
||||
import { useState } from "react";
|
||||
import Logos from "@/components/Logos";
|
||||
|
||||
type PricingProps = {};
|
||||
|
||||
const Pricing = ({}: PricingProps) => {
|
||||
const [monthly, setMonthly] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<Section className="overflow-hidden">
|
||||
<div className="container relative z-2 md:pt-10 lg:pt-16 xl:pt-20">
|
||||
<Heading
|
||||
textAlignClassName="text-center"
|
||||
titleLarge="Pay once, use forever"
|
||||
textLarge="Get started with Brainwave - AI chat app today and experience the power of AI in your conversations!"
|
||||
/>
|
||||
<div className="w-[19rem] mx-auto mb-10 p-0.25 bg-gradient-to-b from-[#D77DEE]/90 to-n-1/15 rounded-xl">
|
||||
<div className="flex p-[0.1875rem] bg-n-8 rounded-[0.6875rem]">
|
||||
<button
|
||||
className={`button flex-1 h-10 rounded-lg transition-colors ${
|
||||
monthly ? "bg-n-6" : ""
|
||||
}`}
|
||||
onClick={() => setMonthly(true)}
|
||||
>
|
||||
monthly
|
||||
</button>
|
||||
<button
|
||||
className={`button flex-1 h-10 rounded-lg transition-colors ${
|
||||
monthly ? "" : "bg-n-6"
|
||||
}`}
|
||||
onClick={() => setMonthly(false)}
|
||||
>
|
||||
annually
|
||||
<span className="ml-2.5 p-1 bg-color-1 rounded">
|
||||
-10%
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<PricingList monthly={monthly} />
|
||||
<Logos className="hidden mt-20 lg:block" />
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Pricing;
|
||||
22
src/templates/PricingPage/index.tsx
Normal file
22
src/templates/PricingPage/index.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
"use client";
|
||||
|
||||
import Layout from "@/components/Layout";
|
||||
import Pricing from "./Pricing";
|
||||
import Comparison from "./Comparison";
|
||||
import Community from "./Community";
|
||||
import Join from "@/components/Join";
|
||||
import Faq from "./Faq";
|
||||
|
||||
const PricingPage = () => {
|
||||
return (
|
||||
<Layout>
|
||||
<Pricing />
|
||||
<Comparison />
|
||||
<Community />
|
||||
<Faq />
|
||||
<Join />
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default PricingPage;
|
||||
Reference in New Issue
Block a user