Преглед изворни кода

refactor & delete json from list

AykutSarac пре 2 година
родитељ
комит
f42bbe6b83
4 измењених фајлова са 92 додато и 28 уклоњено
  1. 53 8
      src/containers/Modals/CloudModal/index.tsx
  2. 3 11
      src/services/db/json.tsx
  3. 27 9
      src/store/useJson.tsx
  4. 9 0
      src/typings/altogic.ts

+ 53 - 8
src/containers/Modals/CloudModal/index.tsx

@@ -5,12 +5,16 @@ 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 { FaTrash } from "react-icons/fa";
+import { IoRocketSharp } from "react-icons/io5";
+import { Button } from "src/components/Button";
 import { Modal, ModalProps } from "src/components/Modal";
 import { Spinner } from "src/components/Spinner";
-import { getAllJson, updateJson } from "src/services/db/json";
+import { deleteJson, getAllJson, saveJson, updateJson } from "src/services/db/json";
+import useJson from "src/store/useJson";
 import useUser from "src/store/useUser";
+import { Json } from "src/typings/altogic";
 import styled from "styled-components";
-import { IoRocketSharp } from "react-icons/io5";
 
 dayjs.extend(relativeTime);
 
@@ -21,8 +25,10 @@ const StyledModalContent = styled.div`
   overflow: auto;
 `;
 
-const StyledJsonCard = styled.a<{ active?: boolean }>`
-  display: block;
+const StyledJsonCard = styled.a<{ active?: boolean; create?: boolean }>`
+  display: ${({ create }) => (create ? "block" : "flex")};
+  align-items: center;
+  justify-content: space-between;
   background: ${({ theme }) => theme.BLACK_SECONDARY};
   border: 2px solid ${({ theme, active }) => (active ? theme.SEAGREEN : theme.BLACK_SECONDARY)};
   border-radius: 5px;
@@ -63,6 +69,10 @@ const StyledModal = styled(Modal)`
   }
 `;
 
+const StyledDeleteButton = styled(Button)`
+  background: transparent;
+`;
+
 const StyledCreateWrapper = styled.div`
   display: flex;
   height: 100%;
@@ -84,7 +94,7 @@ const StyledNameInput = styled.input`
   font-weight: 600;
 `;
 
-const GraphCard: React.FC<{ data: any; refetch: () => void; active: boolean }> = ({
+const GraphCard: React.FC<{ data: Json; refetch: () => void; active: boolean }> = ({
   data,
   refetch,
   active,
@@ -105,6 +115,18 @@ const GraphCard: React.FC<{ data: any; refetch: () => void; active: boolean }> =
     setEditMode(false);
   };
 
+  const onDeleteClick = (e: React.MouseEvent<HTMLButtonElement>) => {
+    e.preventDefault();
+
+    toast
+      .promise(deleteJson(data._id), {
+        loading: "Deleting JSON file...",
+        error: "An error occured while deleting the file!",
+        success: `Deleted ${name}!`,
+      })
+      .then(refetch);
+  };
+
   return (
     <StyledJsonCard
       href={`?json=${data._id}`}
@@ -127,7 +149,6 @@ const GraphCard: React.FC<{ data: any; refetch: () => void; active: boolean }> =
           <StyledTitle
             onClick={e => {
               e.preventDefault();
-              e.stopPropagation();
               setEditMode(true);
             }}
           >
@@ -140,16 +161,40 @@ const GraphCard: React.FC<{ data: any; refetch: () => void; active: boolean }> =
           Last modified {dayjs(data.updatedAt).fromNow()}
         </StyledDetils>
       </StyledInfo>
+      <StyledDeleteButton onClick={onDeleteClick}>
+        <FaTrash />
+      </StyledDeleteButton>
     </StyledJsonCard>
   );
 };
 
 const CreateCard: React.FC<{ reachedLimit: boolean }> = ({ reachedLimit }) => {
+  const { replace } = useRouter();
   const isPremium = useUser(state => state.isPremium());
+  const getJson = useJson(state => state.getJson);
+  const setHasChanges = useJson(state => state.setHasChanges);
+
+  const onCreate = async () => {
+    try {
+      toast.loading("Saving JSON...", { id: "jsonSave" });
+      const res = await saveJson({ data: getJson() });
+
+      if (res.errors && res.errors.items.length > 0) throw res.errors;
+
+      toast.success("JSON saved to cloud", { id: "jsonSave" });
+      setHasChanges(false);
+      replace({ query: { json: res.data._id } });
+    } catch (error: any) {
+      if (error?.items?.length > 0) {
+        return toast.error(error.items[0].message, { id: "jsonSave", duration: 7000 });
+      }
+      toast.error("Failed to save JSON!", { id: "jsonSave" });
+    }
+  };
 
   if (!isPremium && reachedLimit)
     return (
-      <StyledJsonCard href="/pricing">
+      <StyledJsonCard href="/pricing" create>
         <StyledCreateWrapper>
           <IoRocketSharp size="18" />
           You reached max limit, upgrade to premium for more!
@@ -158,7 +203,7 @@ const CreateCard: React.FC<{ reachedLimit: boolean }> = ({ reachedLimit }) => {
     );
 
   return (
-    <StyledJsonCard href="/editor">
+    <StyledJsonCard onClick={onCreate} create>
       <StyledCreateWrapper>
         <AiOutlinePlus size="24" />
         Create New JSON

+ 3 - 11
src/services/db/json.tsx

@@ -1,16 +1,8 @@
 import { compressToBase64 } from "lz-string";
 import { altogic, AltogicResponse } from "src/api/altogic";
+import { Json } from "src/typings/altogic";
 
-type JSON = {
-  _id: string;
-  createdAt: Date;
-  updatedAt: Date;
-  name: string;
-  private: boolean;
-  json: string;
-};
-
-const saveJson = async ({ id, data }): Promise<AltogicResponse<{ _id: string }>> => {
+const saveJson = async ({ id, data }: { id?: string | null; data: string }): Promise<AltogicResponse<{ _id: string }>> => {
   const compressedData = compressToBase64(data);
 
   if (id) {
@@ -24,7 +16,7 @@ const saveJson = async ({ id, data }): Promise<AltogicResponse<{ _id: string }>>
   });
 };
 
-const getAllJson = async (): Promise<AltogicResponse<{ result: JSON[] }>> =>
+const getAllJson = async (): Promise<AltogicResponse<{ result: Json[] }>> =>
   await altogic.endpoint.get(`json`);
 
 const updateJson = async (id: string, data: object) =>

+ 27 - 9
src/store/useJson.tsx

@@ -2,18 +2,11 @@ import { decompressFromBase64 } from "lz-string";
 import toast from "react-hot-toast";
 import { altogic } from "src/api/altogic";
 import { defaultJson } from "src/constants/data";
+import { saveJson as saveJsonDB } from "src/services/db/json";
 import useGraph from "src/store/useGraph";
+import { Json } from "src/typings/altogic";
 import create from "zustand";
 
-interface Json {
-  _id: string;
-  createdAt: string;
-  updatedAt: string;
-  json: string;
-  name: string;
-  private: false;
-}
-
 interface JsonActions {
   setJson: (json: string) => void;
   getJson: () => string;
@@ -21,6 +14,7 @@ interface JsonActions {
   fetchJson: (jsonId: string | string[] | undefined) => void;
   setError: (hasError: boolean) => void;
   setHasChanges: (hasChanges: boolean) => void;
+  saveJson: (isNew?: boolean) => Promise<string | undefined>;
 }
 
 const initialStates = {
@@ -80,6 +74,30 @@ const useJson = create<JsonStates & JsonActions>()((set, get) => ({
     useGraph.getState().setGraph(json);
     set({ json, hasChanges: true });
   },
+  saveJson: async (isNew = true) => {
+    try {
+      const url = new URL(window.location.href);
+      const params = new URLSearchParams(url.search);
+      const jsonQuery = params.get("json");
+
+      toast.loading("Saving JSON...", { id: "jsonSave" });
+      const res = await saveJsonDB({ id: isNew ? undefined : jsonQuery, data: get().json });
+
+      if (res.errors && res.errors.items.length > 0) throw res.errors;
+
+      toast.success("JSON saved to cloud", { id: "jsonSave" });
+      set({ hasChanges: false });
+      return res.data._id;
+    } catch (error: any) {
+      if (error?.items?.length > 0) {
+        toast.error(error.items[0].message, { id: "jsonSave", duration: 5000 });
+        return undefined;
+      }
+
+      toast.error("Failed to save JSON!", { id: "jsonSave" });
+      return undefined;
+    }
+  },
   setError: (hasError: boolean) => set({ hasError }),
   setHasChanges: (hasChanges: boolean) => set({ hasChanges }),
 }));

+ 9 - 0
src/typings/altogic.ts

@@ -11,6 +11,15 @@ export interface User {
 }
 
 
+export interface Json {
+  _id: string;
+  createdAt: string;
+  updatedAt: string;
+  json: string;
+  name: string;
+  private: false;
+}
+
 export enum UserType {
   "DEFAULT" = 0,
   "PREMIUM" = 1