Sfoglia il codice sorgente

update pricing styles

AykutSarac 2 anni fa
parent
commit
319abe039b

+ 30 - 7
src/containers/Editor/BottomBar.tsx

@@ -53,10 +53,15 @@ const StyledBottomBarItem = styled.button`
   font-weight: 400;
   color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
 
-  &:hover {
+  &:hover:not(&:disabled) {
     background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
     color: ${({ theme }) => theme.INTERACTIVE_HOVER};
   }
+
+  &:disabled {
+    opacity: 0.4;
+    cursor: progress;
+  }
 `;
 
 const StyledImg = styled.img<{ light: boolean }>`
@@ -74,6 +79,7 @@ export const BottomBar = () => {
   const setVisible = useModal(state => state.setVisible);
   const setHasChanges = useJson(state => state.setHasChanges);
   const [isPrivate, setIsPrivate] = React.useState(false);
+  const [isUpdating, setIsUpdating] = React.useState(false);
 
   React.useEffect(() => {
     setIsPrivate(data?.private ?? false);
@@ -84,6 +90,7 @@ export const BottomBar = () => {
 
     if (hasChanges) {
       try {
+        setIsUpdating(true);
         toast.loading("Saving JSON...", { id: "jsonSave" });
         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" });
+      } finally {
+        setIsUpdating(false);
       }
     }
   }, [getJson, hasChanges, query.json, replace, setHasChanges, setVisible, user]);
@@ -107,10 +116,24 @@ export const BottomBar = () => {
     else setVisible("login")(true);
   };
 
-  const setPrivate = () => {
-    if (!query.json) return handleSaveJson();
-    setIsPrivate(!isPrivate);
-    updateJson(query.json as string, { private: !isPrivate });
+  const setPrivate = async () => {
+    try {
+      if (!query.json) return handleSaveJson();
+      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 (
@@ -120,14 +143,14 @@ export const BottomBar = () => {
           <VscAccount />
           {user ? user.name : "Login"}
         </StyledBottomBarItem>
-        <StyledBottomBarItem onClick={handleSaveJson}>
+        <StyledBottomBarItem onClick={handleSaveJson} disabled={isUpdating}>
           {hasChanges ? <AiOutlineCloudUpload /> : <AiOutlineCloudSync />}
           {hasChanges ? "Unsaved Changes" : "Saved"}
         </StyledBottomBarItem>
         {data && (
           <>
             {typeof data.private !== "undefined" && (
-              <StyledBottomBarItem onClick={setPrivate}>
+              <StyledBottomBarItem onClick={setPrivate} disabled={isUpdating}>
                 {isPrivate ? <AiOutlineLock /> : <AiOutlineUnlock />}
                 {isPrivate ? "Private" : "Public"}
               </StyledBottomBarItem>

+ 2 - 0
src/containers/Home/index.tsx

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

+ 6 - 2
src/containers/Modals/AccountModal/index.tsx

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

+ 24 - 2
src/containers/Modals/CloudModal/index.tsx

@@ -4,7 +4,13 @@ import { useQuery } from "@tanstack/react-query";
 import dayjs from "dayjs";
 import relativeTime from "dayjs/plugin/relativeTime";
 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 { IoRocketSharp } from "react-icons/io5";
 import { Button } from "src/components/Button";
@@ -94,6 +100,16 @@ const StyledNameInput = styled.input`
   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 }> = ({
   data,
   refetch,
@@ -241,7 +257,13 @@ export const CloudModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
           )}
         </StyledModalContent>
       </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>
   );
 };

+ 129 - 0
src/containers/PricingCards/index.tsx

@@ -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>
+  );
+};

+ 5 - 94
src/pages/pricing.tsx

@@ -1,6 +1,7 @@
 import React from "react";
 import { Button } from "src/components/Button";
 import { Footer } from "src/components/Footer";
+import { PricingCards } from "src/containers/PricingCards";
 import styled from "styled-components";
 
 const StyledPageWrapper = styled.div`
@@ -13,108 +14,18 @@ const StyledHeroSection = styled.section`
   flex-direction: column;
   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 = () => {
   return (
     <>
       <StyledPageWrapper>
+        <Button href="/" link>
+          &lt; Go Back
+        </Button>
         <StyledHeroSection>
           <img src="assets/icon.png" alt="json crack" width="400" />
           <h1>Premium</h1>
         </StyledHeroSection>
-        <StyledPricingSection>
-          <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>
+        <PricingCards />
       </StyledPageWrapper>
       <Footer />
     </>