add premium page

This commit is contained in:
AykutSarac 2024-08-11 02:43:28 +03:00
parent 3939c9842f
commit 6742196e92
No known key found for this signature in database
7 changed files with 516 additions and 124 deletions

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 100" fill="#fff"><path d="M0 0v80l227.5 18c12.1 1 22.5-8.6 22.5-20.7s10.4-21.8 22.5-20.8l205 16.3c12.1 1 22.5-8.6 22.5-20.8s10.4-21.7 22.5-20.8l205 16.3c12.1 1 22.5-8.6 22.5-20.8S760.4 5 772.5 6L1000 24V0H0Z"></path></svg>

After

Width:  |  Height:  |  Size: 280 B

View File

@ -1,119 +1,22 @@
import React from "react";
import { AspectRatio, Flex, Paper, SegmentedControl, Stack, Text } from "@mantine/core";
import styled from "styled-components";
const StyledPreviewWrapper = styled.div`
margin: 0 auto;
position: relative;
padding: 5rem 0;
`;
const features = [
{
label: "Fast & Compact",
value: "1",
},
{
label: "Search",
value: "2",
},
{
label: "Edit",
value: "3",
},
{
label: "Customize",
value: "5",
},
{
label: "Compare",
value: "6",
},
{
label: "AI-Powered Assistant",
value: "7",
},
];
import { Container, Image } from "@mantine/core";
export const HeroPreview = () => {
const [selectedFeature, setSelectedFeature] = React.useState("1");
return (
<StyledPreviewWrapper id="preview">
<Flex
gap="lg"
direction={{
base: "column",
xs: "row",
}}
<Container component="section" id="preview" fluid mx="lg">
<Image
src="./assets/preview/free.webp"
maw={1200}
mx="auto"
maw={{
base: "90%",
xs: "85%",
alt="JSON Crack editor preview"
style={{
borderRadius: 10,
overflow: "hidden",
border: "1px solid #c1c1c1",
outline: "1px solid #c1c1c1",
outlineOffset: "6px",
}}
justify="center"
>
<Stack>
<SegmentedControl
data={features}
value={selectedFeature}
onChange={setSelectedFeature}
orientation="vertical"
withItemsBorders={false}
bg="transparent"
color="orange.7"
size="lg"
visibleFrom="sm"
styles={{
control: {
background: "rgba(168, 168, 168, 0.2)",
borderRadius: "4px",
},
root: {
gap: "24px",
},
}}
/>
</Stack>
<Stack w="100%">
<Paper
p={0}
w="100%"
maw={1440}
h="fit-content"
radius="lg"
shadow="xl"
bg="transparent"
style={{
overflow: "hidden",
border: "1px solid #c1c1c1",
outline: "1px solid #c1c1c1",
outlineOffset: "4px",
}}
>
<AspectRatio ratio={1440 / 760} maw={1440} w="100%" h="100%">
<video
autoPlay
muted
loop
preload="auto"
playsInline
poster={`./assets/preview/${selectedFeature}.webp`}
key={selectedFeature}
style={{ display: "block" }}
>
<source
src={`https://app.jsoncrack.com/assets/videos/p${selectedFeature}.mp4`}
type="video/mp4"
/>
</video>
</AspectRatio>
</Paper>
<Text c="gray.6" fz="sm" ta="center">
Previews are from the Premium version of the app
</Text>
</Stack>
</Flex>
</StyledPreviewWrapper>
/>
</Container>
);
};

View File

@ -1,5 +1,6 @@
import React from "react";
import { Manrope } from "next/font/google";
import Link from "next/link";
import { Stack, Flex, Badge, Button, Text } from "@mantine/core";
import styled from "styled-components";
import { IoSparkles } from "react-icons/io5";
@ -166,6 +167,20 @@ export const HeroSection = () => {
>
Start using free
</Button>
<Button
component={Link}
color="dark"
prefetch={false}
href="/premium"
size="lg"
radius="sm"
fw={500}
fz="md"
mt="lg"
leftSection={<IoSparkles />}
>
Explore Premium
</Button>
</Flex>
<Text c="gray.6" size="xs" mt="-10">
No registration needed.

View File

@ -0,0 +1,117 @@
import React from "react";
import { AspectRatio, Container, Flex, Paper, SegmentedControl, Stack, Title } from "@mantine/core";
const features = [
{
label: "Fast & Compact",
value: "1",
},
{
label: "Search",
value: "2",
},
{
label: "Edit",
value: "3",
},
{
label: "Customize",
value: "5",
},
{
label: "Compare",
value: "6",
},
{
label: "AI-Powered Assistant",
value: "7",
},
];
export const PremiumPreview = () => {
const [selectedFeature, setSelectedFeature] = React.useState("1");
return (
<Container component="section" id="preview" fluid>
<Container size="xl">
<Title
fz={{
base: 32,
xs: 38,
}}
order={2}
mt={100}
mb={60}
>
Discover the features
</Title>
<Flex
gap="lg"
direction={{
base: "column",
xs: "row",
}}
justify="center"
>
<Stack>
<SegmentedControl
data={features}
value={selectedFeature}
onChange={setSelectedFeature}
orientation="vertical"
withItemsBorders={false}
bg="transparent"
color="dark"
size="lg"
visibleFrom="sm"
styles={{
control: {
background: "rgba(168, 168, 168, 0.2)",
borderRadius: "4px",
},
root: {
gap: "24px",
},
}}
/>
</Stack>
<Stack w="100%">
<Paper
p={0}
w="100%"
maw={1440}
h="fit-content"
radius="lg"
shadow="xl"
bg="transparent"
style={{
overflow: "hidden",
border: "1px solid #c1c1c1",
outline: "1px solid #c1c1c1",
outlineOffset: "4px",
}}
>
<AspectRatio ratio={1440 / 760} maw={1440} w="100%" h="100%">
<video
autoPlay
muted
loop
preload="auto"
playsInline
poster={`./assets/preview/${selectedFeature}.webp`}
key={selectedFeature}
style={{ display: "block" }}
>
<source
src={`https://app.jsoncrack.com/assets/videos/p${selectedFeature}.mp4`}
type="video/mp4"
/>
</video>
</AspectRatio>
</Paper>
</Stack>
</Flex>
</Container>
</Container>
);
};

View File

@ -1,4 +1,5 @@
import React from "react";
import Link from "next/link";
import { Title, Overlay, Image, Container, Flex, Box, List, Button } from "@mantine/core";
import styled from "styled-components";
import { ReactCompareSlider, ReactCompareSliderHandle } from "react-compare-slider";
@ -68,17 +69,17 @@ export const PremiumVsFree = () => {
<List.Item>Beautiful UI and very smooth navigation</List.Item>
</List>
<Button
component={Link}
href="/premium"
display="block"
w="fit-content"
miw={200}
variant="filled"
color="orange"
mt="md"
size="md"
component="a"
href="/#preview"
size="lg"
>
See preview!
See all features
</Button>
</Box>

View File

@ -1,10 +1,9 @@
import React from "react";
import Link from "next/link";
import { Button, Title } from "@mantine/core";
import styled from "styled-components";
import { MdChevronRight } from "react-icons/md";
import { JSONCrackLogo } from "src/layout/JsonCrackLogo";
import { gaEvent } from "src/lib/utils/gaEvent";
import useModal from "src/store/useModal";
const StyledNotSupported = styled.div`
position: relative;
@ -156,13 +155,11 @@ const StyledContent = styled.div`
`;
export const NotSupported = () => {
const setVisible = useModal(state => state.setVisible);
return (
<StyledNotSupported>
<StyledContent>
<Title mb="lg" style={{ pointerEvents: "none" }}>
<JSONCrackLogo fontSize="4rem" />
<JSONCrackLogo fontSize="4rem" style={{ color: "black" }} />
</Title>
<StyledInfo>
Upgrade to premium for larger data size support. The free version is incapable of handling
@ -170,10 +167,9 @@ export const NotSupported = () => {
</StyledInfo>
<Button
onClick={() => {
gaEvent("Premium View", "click upgrade premium");
setVisible("upgrade")(true);
}}
component={Link}
href="/premium"
target="_blank"
mt="lg"
size="lg"
fw="bolder"

359
src/pages/premium.tsx Normal file
View File

@ -0,0 +1,359 @@
import React from "react";
import {
Container,
Box,
Title,
Button,
Image,
Text,
SimpleGrid,
Paper,
ThemeIcon,
Flex,
Badge,
Group,
Radio,
Stack,
} from "@mantine/core";
import styled from "styled-components";
import { AiOutlineInfoCircle } from "react-icons/ai";
import { GrMultiple } from "react-icons/gr";
import { IoSparkles } from "react-icons/io5";
import {
MdChevronRight,
MdCompare,
MdOutlinePhotoSizeSelectSmall,
MdOutlineTimer,
MdPalette,
MdRocketLaunch,
MdSpeed,
} from "react-icons/md";
import { RiRobot2Line } from "react-icons/ri";
import { PremiumPreview } from "src/containers/Landing/PremiumPreview";
import Layout from "src/layout/Layout";
import { gaEvent } from "src/lib/utils/gaEvent";
import { PRICING, purchaseLinks } from "./pricing";
const StyledRadioCard = styled(Radio.Card)`
border-width: 2px;
border-color: #efefef;
min-width: 450px;
transition: 0.2s;
background: white;
&[data-checked] {
border-color: #535353;
}
&:hover:not([data-checked]) {
border-color: #b9b9b9;
background: #f9f9f9;
}
@media only screen and (max-width: 600px) {
min-width: unset;
max-width: 100%;
}
`;
const Premium = () => {
const [plan, setPlan] = React.useState("monthly");
const getUpgradeLink = () => {
const link = new URL(purchaseLinks[plan]);
gaEvent("Premium Page", "click select", plan);
return link.toString();
};
return (
<Layout>
<Container
pos="relative"
pt={100}
pb={200}
fluid
style={{
background:
"linear-gradient(165deg, #000000, #010101, #080808, #171717, #272727, #383838, #4a4a4a, #5d5d5d)",
}}
>
<Box mx="auto" maw={600}>
<Title
order={1}
c="white"
fz={{
base: 38,
xs: 50,
}}
>
More than just a JSON Viewer.
</Title>
<Text
c="gray.3"
mt={32}
fz={{
base: 16,
xs: 20,
}}
>
Rebuilt from the ground up now faster, more powerful, and more visually stunning.
</Text>
<Button
component="a"
href="#pricing"
mt="lg"
variant="white"
color="gray"
radius="md"
size="lg"
rightSection={<MdChevronRight size={30} />}
>
Get it now
</Button>
<Image
pos="absolute"
bottom={-1}
left={0}
src="./assets/premium-divider.svg"
width="100%"
alt="divider"
style={{
transform: "scaleY(-1) scaleX(-1)",
}}
/>
</Box>
</Container>
<PremiumPreview />
<Container fluid bg="dark" my={120} py={40}>
<Container size="xl">
<Title
fz={{
base: 28,
xs: 36,
}}
order={2}
c="white"
>
Built for everyone.
</Title>
<Text c="gray.3" fz="xl" mb={40}>
Zero technical knowledge required.
</Text>
<SimpleGrid
cols={{
base: 1,
xs: 2,
md: 4,
}}
spacing="xl"
>
<Paper bg="gray.8" p="lg" radius="md">
<Flex gap="sm" align="center" justify="center" direction="column">
<ThemeIcon radius="xl" size="xl" variant="light" color="blue.3">
<Text fz="sm" fw={500}>
4 MB
</Text>
</ThemeIcon>
<Title ta="center" c="white" order={3}>
Larger Data
</Title>
<Text c="gray.3">From 300KB to ~4MB upgraded data size.</Text>
</Flex>
</Paper>
<Paper bg="gray.8" p="lg" radius="md">
<Flex gap="sm" align="center" justify="center" direction="column">
<ThemeIcon radius="xl" size="xl" variant="light" color="violet.1">
<MdOutlinePhotoSizeSelectSmall size="30" />
</ThemeIcon>
<Title ta="center" c="white" order={3}>
Compact Design
</Title>
<Text c="gray.3">
50% less graph size. Get rid of the redundant data and nodes, focus on what&apos;s
important.
</Text>
</Flex>
</Paper>
<Paper bg="gray.8" p="lg" radius="md">
<Flex gap="sm" align="center" justify="center" direction="column">
<ThemeIcon radius="xl" size="xl" variant="light" color="green.3">
<MdSpeed size="30" />
</ThemeIcon>
<Title ta="center" c="white" order={3}>
Faster
</Title>
<Text c="gray.3">
Load data faster. Navigate faster. Search faster. Everything is faster than ever.
</Text>
</Flex>
</Paper>
<Paper bg="gray.8" p="lg" radius="md">
<Flex gap="sm" align="center" justify="center" direction="column">
<ThemeIcon radius="xl" size="xl" variant="light" color="orange.2">
<MdCompare size="30" />
</ThemeIcon>
<Title ta="center" c="white" order={3}>
Compare
</Title>
<Text c="gray.3">
Compare two data, highlight the differences directly on the graphs.
</Text>
</Flex>
</Paper>
<Paper bg="gray.8" p="lg" radius="md">
<Flex gap="sm" align="center" justify="center" direction="column">
<ThemeIcon radius="xl" size="xl" variant="light" color="violet.3">
<IoSparkles size="22" />
</ThemeIcon>
<Title ta="center" c="white" order={3}>
Edit Directly
</Title>
<Text c="gray.3">
Modify your data directly on the graph. No more switching between the editor and
the graph.
</Text>
</Flex>
</Paper>
<Paper bg="gray.8" p="lg" radius="md">
<Flex gap="sm" align="center" justify="center" direction="column">
<ThemeIcon radius="xl" size="xl" variant="light" color="yellow.2">
<MdPalette size="30" />
</ThemeIcon>
<Title ta="center" c="white" order={3}>
Customize
</Title>
<Text c="gray.3">
Customize the graph&apos;s colors to align with your brand or personal
preferences.
</Text>
</Flex>
</Paper>
<Paper bg="gray.8" p="lg" radius="md">
<Flex gap="sm" align="center" justify="center" direction="column">
<ThemeIcon radius="xl" size="xl" variant="light" color="grape.2">
<GrMultiple size="22" />
</ThemeIcon>
<Title ta="center" c="white" order={3}>
Tabs
</Title>
<Text c="gray.3">
Open multiple tabs, navigate between them easily. Save up to 200 documents to the
cloud.
</Text>
</Flex>
</Paper>
<Paper bg="gray.8" p="lg" radius="md">
<Flex gap="sm" align="center" justify="center" direction="column">
<ThemeIcon radius="xl" size="xl" variant="light" color="gray.1">
<RiRobot2Line size="22" />
</ThemeIcon>
<Title ta="center" c="white" order={3}>
AI-Powered
</Title>
<Text c="gray.3">
Ask it to translate your fields, filter out by age or name includes, and more.
</Text>
</Flex>
</Paper>
</SimpleGrid>
</Container>
</Container>
<Container pt="xl" component="section" id="pricing" size="xl">
<Title
maw={800}
fw={500}
mx="auto"
ta="center"
c="dark"
fz={{
base: 28,
xs: 40,
}}
order={2}
>
Keep your expectations high.
</Title>
<Radio.Group maw={600} mx="auto" mt="xl" value={plan} onChange={setPlan}>
<Stack>
<StyledRadioCard value="monthly" radius="lg" px="xl" py="md">
<Group align="center" justify="space-between">
<Flex align="center" gap="xs">
<Text fz="xl" c="gray.7" fw={600}>
Monthly
</Text>
</Flex>
<Flex fw={500} align="baseline" fz="sm" c="gray.5">
<Text fw={600} fz="xl" c="gray.7">
${PRICING.MONTHLY}
</Text>
/month
</Flex>
</Group>
</StyledRadioCard>
<StyledRadioCard value="annual" radius="lg" px="xl" py="md">
<Group align="center" justify="space-between">
<Flex align="center" gap="xs">
<Text fz="xl" c="gray.7" fw={600}>
Yearly
</Text>
</Flex>
<Flex fw={500} align="baseline" fz="sm" c="gray.5">
<Text fw={600} fz="xl" c="gray.7">
${PRICING.ANNUAL * 12}
</Text>
/year
</Flex>
</Group>
</StyledRadioCard>
<StyledRadioCard value="ltd" radius="lg" px="xl" py="md">
<Group align="center" justify="space-between">
<Flex align="center" gap="xs">
<Text fz="xl" c="gray.7" fw={600}>
Lifetime
</Text>
<Badge
variant="light"
size="sm"
radius="lg"
color="red"
leftSection={<MdOutlineTimer size="12" />}
>
Limited
</Badge>
</Flex>
<Flex fw={500} align="baseline" fz="sm" c="gray.5">
<Text fw={600} fz="xl" c="gray.7">
${PRICING.LTD}
</Text>
/lifetime
</Flex>
</Group>
</StyledRadioCard>
</Stack>
<Button
component="a"
href={getUpgradeLink()}
target="_blank"
color="dark"
fullWidth
mt="xl"
size="xl"
radius="md"
leftSection={<MdRocketLaunch />}
>
Upgrade
</Button>
</Radio.Group>
<Flex pt="sm" c="dimmed" justify="center" align="center" gap={4}>
<AiOutlineInfoCircle />
<Text size="xs">
Payment email must be matching with the account registered to the JSON Crack.
</Text>
</Flex>
</Container>
</Layout>
);
};
export default Premium;