From e4217fe15f276f8e98785c6aa7c36de4f01d4701 Mon Sep 17 00:00:00 2001 From: !MAD! <61308761+iMADi-ARCH@users.noreply.github.com> Date: Tue, 23 May 2023 11:45:13 +0530 Subject: [PATCH] Mad/UI improvements in /explore (#127) * feature: delete file * feature: consume /explore/file_name to view details of an uploaded document * feature: optimistic update when deleting file * feature: Loading state for /explore * style: Exit animation * style: responsive card --- frontend/app/components/ui/Button.tsx | 4 +- frontend/app/components/ui/Modal.tsx | 19 ++++- frontend/app/components/ui/Spinner.tsx | 10 +++ frontend/app/explore/DocumentItem.tsx | 34 -------- .../app/explore/DocumentItem/DocumentData.tsx | 37 ++++++++ frontend/app/explore/DocumentItem/index.tsx | 84 +++++++++++++++++++ frontend/app/explore/page.tsx | 45 ++++++---- 7 files changed, 179 insertions(+), 54 deletions(-) create mode 100644 frontend/app/components/ui/Spinner.tsx delete mode 100644 frontend/app/explore/DocumentItem.tsx create mode 100644 frontend/app/explore/DocumentItem/DocumentData.tsx create mode 100644 frontend/app/explore/DocumentItem/index.tsx diff --git a/frontend/app/components/ui/Button.tsx b/frontend/app/components/ui/Button.tsx index 88453fff5..bb283a236 100644 --- a/frontend/app/components/ui/Button.tsx +++ b/frontend/app/components/ui/Button.tsx @@ -19,6 +19,8 @@ const ButtonVariants = cva( tertiary: "text-black dark:text-white bg-transparent py-2 px-4", secondary: "border border-black dark:border-white bg-white dark:bg-black text-black dark:text-white focus:bg-black dark:focus:bg-white hover:bg-black dark:hover:bg-white hover:text-white dark:hover:text-black focus:text-white transition-colors py-2 px-4 shadow-none", + danger: + "border border-red-500 hover:bg-red-500 hover:text-white transition-colors", }, brightness: { dim: "", @@ -55,5 +57,5 @@ const Button: FC = forwardRef( } ); -Button.displayName = 'Button'; +Button.displayName = "Button"; export default Button; diff --git a/frontend/app/components/ui/Modal.tsx b/frontend/app/components/ui/Modal.tsx index 663736546..ef521b04c 100644 --- a/frontend/app/components/ui/Modal.tsx +++ b/frontend/app/components/ui/Modal.tsx @@ -10,9 +10,16 @@ interface ModalProps { desc: string; children?: ReactNode; Trigger: ReactNode; + CloseTrigger?: ReactNode; } -const Modal: FC = ({ title, desc, children, Trigger }) => { +const Modal: FC = ({ + title, + desc, + children, + Trigger, + CloseTrigger, +}) => { const [open, setOpen] = useState(false); return ( @@ -51,9 +58,13 @@ const Modal: FC = ({ title, desc, children, Trigger }) => { {children} - + {CloseTrigger ? ( + CloseTrigger + ) : ( + + )} diff --git a/frontend/app/components/ui/Spinner.tsx b/frontend/app/components/ui/Spinner.tsx new file mode 100644 index 000000000..722b54160 --- /dev/null +++ b/frontend/app/components/ui/Spinner.tsx @@ -0,0 +1,10 @@ +import { FC } from "react"; +import { FaSpinner } from "react-icons/fa"; + +interface SpinnerProps {} + +const Spinner: FC = ({}) => { + return ; +}; + +export default Spinner; diff --git a/frontend/app/explore/DocumentItem.tsx b/frontend/app/explore/DocumentItem.tsx deleted file mode 100644 index dd7baface..000000000 --- a/frontend/app/explore/DocumentItem.tsx +++ /dev/null @@ -1,34 +0,0 @@ -"use client"; -import { FC } from "react"; -import { Document } from "./types"; -import Button from "../components/ui/Button"; -import Modal from "../components/ui/Modal"; -import { AnimatedCard } from "../components/ui/Card"; - -interface DocumentProps { - document: Document; -} - -const DocumentItem: FC = ({ document }) => { - return ( - -

{document.name}

- View} - > -
-
{JSON.stringify(document, null, 2)}
-
-
-
- ); -}; - -DocumentItem.displayName = "DocumentItem"; -export default DocumentItem; diff --git a/frontend/app/explore/DocumentItem/DocumentData.tsx b/frontend/app/explore/DocumentItem/DocumentData.tsx new file mode 100644 index 000000000..8a4065b03 --- /dev/null +++ b/frontend/app/explore/DocumentItem/DocumentData.tsx @@ -0,0 +1,37 @@ +import axios from "axios"; +import { Document } from "../types"; + +interface DocumentDataProps { + documentName: string; +} + +const DocumentData = async ({ documentName }: DocumentDataProps) => { + const res = await axios.get( + `${process.env.NEXT_PUBLIC_BACKEND_URL}/explore/${documentName}` + ); + const documents = res.data.documents as any[]; + const doc = documents[0]; + return ( +
+

No. of documents: {documents.length}

+ {/* {documents.map((doc) => ( +
{JSON.stringify(doc)}
+ ))} */} +
+ {documents[0] && + Object.keys(documents[0]).map((k) => { + return ( +
+ + {k.replaceAll("_", " ")} + + {documents[0][k] || "Not Available"} +
+ ); + })} +
+
+ ); +}; + +export default DocumentData; diff --git a/frontend/app/explore/DocumentItem/index.tsx b/frontend/app/explore/DocumentItem/index.tsx new file mode 100644 index 000000000..cd093246e --- /dev/null +++ b/frontend/app/explore/DocumentItem/index.tsx @@ -0,0 +1,84 @@ +"use client"; +import { Document } from "../types"; +import Button from "../../components/ui/Button"; +import Modal from "../../components/ui/Modal"; +import { AnimatedCard } from "../../components/ui/Card"; +import { Dispatch, SetStateAction, Suspense, useState } from "react"; +import axios from "axios"; +import DocumentData from "./DocumentData"; +import Spinner from "@/app/components/ui/Spinner"; + +interface DocumentProps { + document: Document; + setDocuments: Dispatch>; +} + +const DocumentItem = ({ document, setDocuments }: DocumentProps) => { + const [isDeleting, setIsDeleting] = useState(false); + + const deleteDocument = async (name: string) => { + setIsDeleting(true); + try { + console.log(`Deleting Document ${name}`); + const response = await axios.delete( + `${process.env.NEXT_PUBLIC_BACKEND_URL}/explore/${name}` + ); + setDocuments((docs) => docs.filter((doc) => doc.name !== name)); // Optimistic update + } catch (error) { + console.error(`Error deleting ${name}`, error); + } + setIsDeleting(false); + }; + + return ( + +

{document.name}

+
+ {/* VIEW MODAL */} + View} + > + }> + {/* @ts-expect-error */} + + + + + {/* DELETE MODAL */} + + Delete + + } + CloseTrigger={ + + } + > +

{document.name}

+
+
+
+ ); +}; + +DocumentItem.displayName = "DocumentItem"; +export default DocumentItem; diff --git a/frontend/app/explore/page.tsx b/frontend/app/explore/page.tsx index c560011b2..b0dcd10d2 100644 --- a/frontend/app/explore/page.tsx +++ b/frontend/app/explore/page.tsx @@ -5,15 +5,19 @@ import DocumentItem from "./DocumentItem"; import { Document } from "./types"; import Button from "../components/ui/Button"; import Link from "next/link"; +import Spinner from "../components/ui/Spinner"; +import { AnimatePresence } from "framer-motion"; export default function ExplorePage() { const [documents, setDocuments] = useState([]); + const [isPending, setIsPending] = useState(true); useEffect(() => { fetchDocuments(); }, []); const fetchDocuments = async () => { + setIsPending(true); try { console.log( `Fetching documents from ${process.env.NEXT_PUBLIC_BACKEND_URL}/explore` @@ -26,28 +30,39 @@ export default function ExplorePage() { console.error("Error fetching documents", error); setDocuments([]); } + setIsPending(false); }; return (
-
+

Explore Your Brain

View what’s in your second brain

-
- {documents.length !== 0 ? ( - documents.map((document, index) => ( - - )) - ) : ( -
-

Oh No, Your Brain is empty.

- - - -
- )} -
+ {isPending ? ( + + ) : ( +
+ {documents.length !== 0 ? ( + + {documents.map((document) => ( + + ))} + + ) : ( +
+

Oh No, Your Brain is empty.

+ + + +
+ )} +
+ )}
); }