update pricing styles

This commit is contained in:
AykutSarac 2022-12-30 16:49:41 +03:00
parent 321a13ab37
commit 319abe039b
6 changed files with 196 additions and 105 deletions

View File

@ -53,10 +53,15 @@ const StyledBottomBarItem = styled.button`
font-weight: 400; font-weight: 400;
color: ${({ theme }) => theme.INTERACTIVE_NORMAL}; color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
&:hover { &:hover:not(&:disabled) {
background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
color: ${({ theme }) => theme.INTERACTIVE_HOVER}; color: ${({ theme }) => theme.INTERACTIVE_HOVER};
} }
&:disabled {
opacity: 0.4;
cursor: progress;
}
`; `;
const StyledImg = styled.img<{ light: boolean }>` const StyledImg = styled.img<{ light: boolean }>`
@ -74,6 +79,7 @@ export const BottomBar = () => {
const setVisible = useModal(state => state.setVisible); const setVisible = useModal(state => state.setVisible);
const setHasChanges = useJson(state => state.setHasChanges); const setHasChanges = useJson(state => state.setHasChanges);
const [isPrivate, setIsPrivate] = React.useState(false); const [isPrivate, setIsPrivate] = React.useState(false);
const [isUpdating, setIsUpdating] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
setIsPrivate(data?.private ?? false); setIsPrivate(data?.private ?? false);
@ -84,6 +90,7 @@ export const BottomBar = () => {
if (hasChanges) { if (hasChanges) {
try { try {
setIsUpdating(true);
toast.loading("Saving JSON...", { id: "jsonSave" }); toast.loading("Saving JSON...", { id: "jsonSave" });
const res = await saveJson({ id: query.json as string, data: getJson() }); const res = await saveJson({ id: query.json as string, data: getJson() });
@ -98,6 +105,8 @@ export const BottomBar = () => {
} }
toast.error("Failed to save JSON!", { id: "jsonSave" }); toast.error("Failed to save JSON!", { id: "jsonSave" });
} finally {
setIsUpdating(false);
} }
} }
}, [getJson, hasChanges, query.json, replace, setHasChanges, setVisible, user]); }, [getJson, hasChanges, query.json, replace, setHasChanges, setVisible, user]);
@ -107,10 +116,24 @@ export const BottomBar = () => {
else setVisible("login")(true); else setVisible("login")(true);
}; };
const setPrivate = () => { const setPrivate = async () => {
if (!query.json) return handleSaveJson(); try {
setIsPrivate(!isPrivate); if (!query.json) return handleSaveJson();
updateJson(query.json as string, { private: !isPrivate }); if (!isPrivate && user?.type === 0) {
return window.open("https://jsoncrack.com/pricing", "_blank");
}
setIsUpdating(true);
const res = await updateJson(query.json as string, { private: !isPrivate });
if (!res.errors?.items.length) {
setIsPrivate(res.data.private);
toast.success(`Document set to ${isPrivate ? "public" : "private"}.`);
} else throw res.errors;
} catch (error) {
toast.error("An error occured while updating document!");
} finally {
setIsUpdating(false);
}
}; };
return ( return (
@ -120,14 +143,14 @@ export const BottomBar = () => {
<VscAccount /> <VscAccount />
{user ? user.name : "Login"} {user ? user.name : "Login"}
</StyledBottomBarItem> </StyledBottomBarItem>
<StyledBottomBarItem onClick={handleSaveJson}> <StyledBottomBarItem onClick={handleSaveJson} disabled={isUpdating}>
{hasChanges ? <AiOutlineCloudUpload /> : <AiOutlineCloudSync />} {hasChanges ? <AiOutlineCloudUpload /> : <AiOutlineCloudSync />}
{hasChanges ? "Unsaved Changes" : "Saved"} {hasChanges ? "Unsaved Changes" : "Saved"}
</StyledBottomBarItem> </StyledBottomBarItem>
{data && ( {data && (
<> <>
{typeof data.private !== "undefined" && ( {typeof data.private !== "undefined" && (
<StyledBottomBarItem onClick={setPrivate}> <StyledBottomBarItem onClick={setPrivate} disabled={isUpdating}>
{isPrivate ? <AiOutlineLock /> : <AiOutlineUnlock />} {isPrivate ? <AiOutlineLock /> : <AiOutlineUnlock />}
{isPrivate ? "Private" : "Public"} {isPrivate ? "Private" : "Public"}
</StyledBottomBarItem> </StyledBottomBarItem>

View File

@ -18,6 +18,7 @@ import { Sponsors } from "src/components/Sponsors";
import { SupportButton } from "src/components/SupportButton"; import { SupportButton } from "src/components/SupportButton";
import { baseURL } from "src/constants/data"; import { baseURL } from "src/constants/data";
import * as Styles from "./styles"; import * as Styles from "./styles";
import { PricingCards } from "../PricingCards";
const Navbar = () => ( const Navbar = () => (
<Styles.StyledNavbar> <Styles.StyledNavbar>
@ -282,6 +283,7 @@ const Home: React.FC = () => {
<FeaturesSection /> <FeaturesSection />
<GitHubSection /> <GitHubSection />
<EmbedSection /> <EmbedSection />
<PricingCards />
<SupportSection /> <SupportSection />
<SponsorSection /> <SponsorSection />
<SupportButton /> <SupportButton />

View File

@ -80,7 +80,7 @@ const AccountView: React.FC<Pick<ModalProps, "setVisible">> = ({ setVisible }) =
<StyledContainer> <StyledContainer>
ACCOUNT STATUS ACCOUNT STATUS
<div> <div>
{isPremium ? "PREMIUM " : "Normal"} {isPremium ? "PREMIUM " : "Free"}
{isPremium && <MdVerified />} {isPremium && <MdVerified />}
</div> </div>
</StyledContainer> </StyledContainer>
@ -93,7 +93,11 @@ const AccountView: React.FC<Pick<ModalProps, "setVisible">> = ({ setVisible }) =
<div>{user?.signUpAt && new Date(user.signUpAt).toDateString()}</div> <div>{user?.signUpAt && new Date(user.signUpAt).toDateString()}</div>
</StyledContainer> </StyledContainer>
{isPremium ? ( {isPremium ? (
<Button status="DANGER" block> <Button
status="DANGER"
block
onClick={() => window.open("https://patreon.com/jsoncrack", "_blank")}
>
<IoRocketSharp /> <IoRocketSharp />
Cancel Subscription Cancel Subscription
</Button> </Button>

View File

@ -4,7 +4,13 @@ import { useQuery } from "@tanstack/react-query";
import dayjs from "dayjs"; import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime"; import relativeTime from "dayjs/plugin/relativeTime";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { AiOutlineEdit, AiOutlineLock, AiOutlinePlus, AiOutlineUnlock } from "react-icons/ai"; import {
AiOutlineEdit,
AiOutlineInfoCircle,
AiOutlineLock,
AiOutlinePlus,
AiOutlineUnlock,
} from "react-icons/ai";
import { FaTrash } from "react-icons/fa"; import { FaTrash } from "react-icons/fa";
import { IoRocketSharp } from "react-icons/io5"; import { IoRocketSharp } from "react-icons/io5";
import { Button } from "src/components/Button"; import { Button } from "src/components/Button";
@ -94,6 +100,16 @@ const StyledNameInput = styled.input`
font-weight: 600; font-weight: 600;
`; `;
const StyledInfoText = styled.span`
font-size: 10px;
color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
svg {
vertical-align: text-top;
margin-right: 4px;
}
`;
const GraphCard: React.FC<{ data: Json; refetch: () => void; active: boolean }> = ({ const GraphCard: React.FC<{ data: Json; refetch: () => void; active: boolean }> = ({
data, data,
refetch, refetch,
@ -241,7 +257,13 @@ export const CloudModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
)} )}
</StyledModalContent> </StyledModalContent>
</Modal.Content> </Modal.Content>
<Modal.Controls setVisible={setVisible}></Modal.Controls>
<Modal.Controls setVisible={setVisible}>
<StyledInfoText>
<AiOutlineInfoCircle />
Cloud Save feature is for ease-of-access only and recommended to not store sensitive data.
</StyledInfoText>
</Modal.Controls>
</StyledModal> </StyledModal>
); );
}; };

View File

@ -0,0 +1,129 @@
import React from "react";
import { Button } from "src/components/Button";
import styled from "styled-components";
const StyledSectionBody = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
gap: 50px;
align-items: center;
justify-content: space-between;
background: rgba(181, 116, 214, 0.23);
width: 80%;
margin: 5% auto 0;
border-radius: 6px;
padding: 50px;
@media only screen and (max-width: 768px) {
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
padding: 20px;
}
`;
const StyledPricingCard = styled.div<{ premium?: boolean }>`
padding: 6px;
padding-bottom: 0;
width: 100%;
height: 100%;
${({ premium }) =>
premium
? `
background: rgba(255, 5, 214, 0.19);
border-radius: 4px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
border: 1px solid rgba(255, 5, 214, 0.74);`
: `background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.3);`};
`;
const StyledPricingCardTitle = styled.h2`
text-align: center;
font-weight: 800;
font-size: 24px;
`;
const StyledPricingCardPrice = styled.h3`
text-align: center;
font-weight: 600;
font-size: 24px;
color: ${({ theme }) => theme.SILVER};
`;
const StyledPricingCardDetails = styled.ul`
color: ${({ theme }) => theme.TEXT_NORMAL};
line-height: 2.3;
padding: 20px;
`;
const StyledPricingCardDetailsItem = styled.li`
font-weight: 500;
@media only screen and (max-width: 768px) {
font-size: 14px;
}
`;
const StyledButton = styled(Button)`
border: 1px solid white;
`;
const StyledPricingSection = styled.section`
margin: 0 auto;
h1 {
text-align: center;
padding-bottom: 25px;
}
`;
export const PricingCards = () => {
return (
<StyledPricingSection>
<h1>Unlock Full Potential of JSON Crack</h1>
<StyledSectionBody>
<StyledPricingCard>
<StyledPricingCardTitle>Free</StyledPricingCardTitle>
<StyledPricingCardDetails>
<StyledPricingCardDetailsItem>Store up to 15 files</StyledPricingCardDetailsItem>
<StyledPricingCardDetailsItem>
Create short-links for saved JSON files
</StyledPricingCardDetailsItem>
<StyledPricingCardDetailsItem>Embed saved JSON instantly</StyledPricingCardDetailsItem>
</StyledPricingCardDetails>
</StyledPricingCard>
<StyledPricingCard premium>
<StyledPricingCardTitle>Premium</StyledPricingCardTitle>
<StyledPricingCardPrice>$5/mo</StyledPricingCardPrice>
<StyledPricingCardDetails>
<StyledPricingCardDetailsItem>
Create and share up to 200 files
</StyledPricingCardDetailsItem>
<StyledPricingCardDetailsItem>Store private JSON</StyledPricingCardDetailsItem>
<StyledPricingCardDetailsItem>
Get access to JSON Crack API to generate JSON remotely
</StyledPricingCardDetailsItem>
<StyledPricingCardDetailsItem>Everything in previous tier</StyledPricingCardDetailsItem>
</StyledPricingCardDetails>
<StyledButton
href="https://www.patreon.com/jsoncrack"
target="_blank"
status="SUCCESS"
block
link
>
GET IT NOW!
</StyledButton>
</StyledPricingCard>
</StyledSectionBody>
</StyledPricingSection>
);
};

View File

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { Button } from "src/components/Button"; import { Button } from "src/components/Button";
import { Footer } from "src/components/Footer"; import { Footer } from "src/components/Footer";
import { PricingCards } from "src/containers/PricingCards";
import styled from "styled-components"; import styled from "styled-components";
const StyledPageWrapper = styled.div` const StyledPageWrapper = styled.div`
@ -13,108 +14,18 @@ const StyledHeroSection = styled.section`
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
`; `;
const StyledPricingSection = styled.section`
display: flex;
justify-content: space-evenly;
background: rgba(181, 116, 214, 0.23);
width: 60%;
margin: 5% auto 0;
border-radius: 6px;
padding: 40px 20px;
`;
const StyledPricingCard = styled.div<{ premium?: boolean }>`
padding: 6px;
width: 40%;
${({ premium }) =>
premium
? `
background: rgba(255, 5, 214, 0.19);
border-radius: 4px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
border: 1px solid rgba(255, 5, 214, 0.74);`
: `background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.3);`};
`;
const StyledPricingCardTitle = styled.h2`
text-align: center;
font-weight: 800;
font-size: 24px;
`;
const StyledPricingCardPrice = styled.h3`
text-align: center;
font-weight: 600;
font-size: 24px;
color: ${({ theme }) => theme.SILVER};
`;
const StyledPricingCardDetails = styled.ul`
color: ${({ theme }) => theme.TEXT_NORMAL};
line-height: 2.3;
`;
const StyledPricingCardDetailsItem = styled.li`
font-weight: 500;
`;
const StyledButton = styled(Button)`
border: 1px solid white;
`;
const Pricing = () => { const Pricing = () => {
return ( return (
<> <>
<StyledPageWrapper> <StyledPageWrapper>
<Button href="/" link>
&lt; Go Back
</Button>
<StyledHeroSection> <StyledHeroSection>
<img src="assets/icon.png" alt="json crack" width="400" /> <img src="assets/icon.png" alt="json crack" width="400" />
<h1>Premium</h1> <h1>Premium</h1>
</StyledHeroSection> </StyledHeroSection>
<StyledPricingSection> <PricingCards />
<StyledPricingCard>
<StyledPricingCardTitle>Free</StyledPricingCardTitle>
<StyledPricingCardDetails>
<StyledPricingCardDetailsItem>Store up to 20 files</StyledPricingCardDetailsItem>
<StyledPricingCardDetailsItem>
Create short-links for saved JSON files
</StyledPricingCardDetailsItem>
<StyledPricingCardDetailsItem>
Embed saved JSON instantly
</StyledPricingCardDetailsItem>
</StyledPricingCardDetails>
</StyledPricingCard>
<StyledPricingCard premium>
<StyledPricingCardTitle>Premium</StyledPricingCardTitle>
<StyledPricingCardPrice>$5/mo</StyledPricingCardPrice>
<StyledPricingCardDetails>
<StyledPricingCardDetailsItem>
Create and share up to 200 files
</StyledPricingCardDetailsItem>
<StyledPricingCardDetailsItem>Store private JSON</StyledPricingCardDetailsItem>
<StyledPricingCardDetailsItem>
Premium role at Discord server
</StyledPricingCardDetailsItem>
<StyledPricingCardDetailsItem>
Get access to JSON Crack API to generate JSON remotely
</StyledPricingCardDetailsItem>
<StyledPricingCardDetailsItem>
Everything in previous tier
</StyledPricingCardDetailsItem>
</StyledPricingCardDetails>
<StyledButton href="https://www.patreon.com/jsoncrack" status="SUCCESS" block link>
GET IT NOW!
</StyledButton>
</StyledPricingCard>
</StyledPricingSection>
</StyledPageWrapper> </StyledPageWrapper>
<Footer /> <Footer />
</> </>