mirror of
https://github.com/StanGirard/quivr.git
synced 2024-12-24 20:03:41 +03:00
Fix/poplayout warning (#194)
* fix(auth): use redirect instead of router * fix(auth): use router for after render redirects * fix(anims): forward ref in file component * fix(anims): forward ref in document item * fix(CI)
This commit is contained in:
parent
989f8de4ca
commit
3769795f9e
@ -3,7 +3,14 @@ import Spinner from "@/app/components/ui/Spinner";
|
||||
import { useSupabase } from "@/app/supabase-provider";
|
||||
import { useToast } from "@/lib/hooks/useToast";
|
||||
import axios from "axios";
|
||||
import { Dispatch, SetStateAction, Suspense, useState } from "react";
|
||||
import {
|
||||
Dispatch,
|
||||
RefObject,
|
||||
SetStateAction,
|
||||
Suspense,
|
||||
forwardRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import Button from "../../components/ui/Button";
|
||||
import { AnimatedCard } from "../../components/ui/Card";
|
||||
import Modal from "../../components/ui/Modal";
|
||||
@ -15,83 +22,86 @@ interface DocumentProps {
|
||||
setDocuments: Dispatch<SetStateAction<Document[]>>;
|
||||
}
|
||||
|
||||
const DocumentItem = ({ document, setDocuments }: DocumentProps) => {
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const { publish } = useToast();
|
||||
const { session } = useSupabase();
|
||||
if (!session) {
|
||||
throw new Error("User session not found");
|
||||
}
|
||||
|
||||
const deleteDocument = async (name: string) => {
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
await axios.delete(
|
||||
`${process.env.NEXT_PUBLIC_BACKEND_URL}/explore/${name}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.access_token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
setDocuments((docs) => docs.filter((doc) => doc.name !== name)); // Optimistic update
|
||||
publish({ variant: "success", text: `${name} deleted.` });
|
||||
} catch (error) {
|
||||
console.error(`Error deleting ${name}`, error);
|
||||
const DocumentItem = forwardRef(
|
||||
({ document, setDocuments }: DocumentProps, forwardedRef) => {
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const { publish } = useToast();
|
||||
const { session } = useSupabase();
|
||||
if (!session) {
|
||||
throw new Error("User session not found");
|
||||
}
|
||||
setIsDeleting(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<AnimatedCard
|
||||
initial={{ x: -64, opacity: 0 }}
|
||||
animate={{ x: 0, opacity: 1 }}
|
||||
exit={{ x: 64, opacity: 0 }}
|
||||
layout
|
||||
className="flex flex-col sm:flex-row sm:items-center justify-between w-full p-5 gap-5"
|
||||
>
|
||||
<p className="text-lg leading-tight max-w-sm">{document.name}</p>
|
||||
<div className="flex gap-2 self-end">
|
||||
{/* VIEW MODAL */}
|
||||
<Modal
|
||||
title={document.name}
|
||||
desc={""}
|
||||
Trigger={<Button className="">View</Button>}
|
||||
>
|
||||
<Suspense fallback={<Spinner />}>
|
||||
{/* @ts-expect-error TODO: check if DocumentData component can be sync */}
|
||||
<DocumentData documentName={document.name} />
|
||||
</Suspense>
|
||||
</Modal>
|
||||
const deleteDocument = async (name: string) => {
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
await axios.delete(
|
||||
`${process.env.NEXT_PUBLIC_BACKEND_URL}/explore/${name}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${session.access_token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
setDocuments((docs) => docs.filter((doc) => doc.name !== name)); // Optimistic update
|
||||
publish({ variant: "success", text: `${name} deleted.` });
|
||||
} catch (error) {
|
||||
console.error(`Error deleting ${name}`, error);
|
||||
}
|
||||
setIsDeleting(false);
|
||||
};
|
||||
|
||||
{/* DELETE MODAL */}
|
||||
<Modal
|
||||
title={"Confirm"}
|
||||
desc={`Do you really want to delete?`}
|
||||
Trigger={
|
||||
<Button isLoading={isDeleting} variant={"danger"} className="">
|
||||
Delete
|
||||
</Button>
|
||||
}
|
||||
CloseTrigger={
|
||||
<Button
|
||||
variant={"danger"}
|
||||
isLoading={isDeleting}
|
||||
onClick={() => {
|
||||
deleteDocument(document.name);
|
||||
}}
|
||||
className="self-end"
|
||||
>
|
||||
Delete forever
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<p>{document.name}</p>
|
||||
</Modal>
|
||||
</div>
|
||||
</AnimatedCard>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<AnimatedCard
|
||||
initial={{ x: -64, opacity: 0 }}
|
||||
animate={{ x: 0, opacity: 1 }}
|
||||
exit={{ x: 64, opacity: 0 }}
|
||||
layout
|
||||
ref={forwardedRef as RefObject<HTMLDivElement>}
|
||||
className="flex flex-col sm:flex-row sm:items-center justify-between w-full p-5 gap-5"
|
||||
>
|
||||
<p className="text-lg leading-tight max-w-sm">{document.name}</p>
|
||||
<div className="flex gap-2 self-end">
|
||||
{/* VIEW MODAL */}
|
||||
<Modal
|
||||
title={document.name}
|
||||
desc={""}
|
||||
Trigger={<Button className="">View</Button>}
|
||||
>
|
||||
<Suspense fallback={<Spinner />}>
|
||||
{/* @ts-expect-error TODO: check if DocumentData component can be sync */}
|
||||
<DocumentData documentName={document.name} />
|
||||
</Suspense>
|
||||
</Modal>
|
||||
|
||||
{/* DELETE MODAL */}
|
||||
<Modal
|
||||
title={"Confirm"}
|
||||
desc={`Do you really want to delete?`}
|
||||
Trigger={
|
||||
<Button isLoading={isDeleting} variant={"danger"} className="">
|
||||
Delete
|
||||
</Button>
|
||||
}
|
||||
CloseTrigger={
|
||||
<Button
|
||||
variant={"danger"}
|
||||
isLoading={isDeleting}
|
||||
onClick={() => {
|
||||
deleteDocument(document.name);
|
||||
}}
|
||||
className="self-end"
|
||||
>
|
||||
Delete forever
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<p>{document.name}</p>
|
||||
</Modal>
|
||||
</div>
|
||||
</AnimatedCard>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
DocumentItem.displayName = "DocumentItem";
|
||||
export default DocumentItem;
|
||||
|
@ -41,7 +41,7 @@ export default function ExplorePage() {
|
||||
setIsPending(false);
|
||||
};
|
||||
fetchDocuments();
|
||||
}, []);
|
||||
}, [session.access_token]);
|
||||
|
||||
return (
|
||||
<main>
|
||||
|
@ -61,7 +61,7 @@ export const useCrawler = () => {
|
||||
} finally {
|
||||
setCrawling(false);
|
||||
}
|
||||
}, [session.access_token]);
|
||||
}, [session.access_token, publish]);
|
||||
|
||||
return {
|
||||
isCrawling,
|
||||
|
@ -1,41 +1,47 @@
|
||||
import { motion } from "framer-motion";
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
import { Dispatch, RefObject, SetStateAction, forwardRef } from "react";
|
||||
import { MdClose } from "react-icons/md";
|
||||
|
||||
export const FileComponent = ({
|
||||
file,
|
||||
setFiles,
|
||||
}: {
|
||||
interface FileComponentProps {
|
||||
file: File;
|
||||
setFiles: Dispatch<SetStateAction<File[]>>;
|
||||
}) => {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ x: "-10%", opacity: 0 }}
|
||||
animate={{ x: "0%", opacity: 1 }}
|
||||
exit={{ x: "10%", opacity: 0 }}
|
||||
layout
|
||||
className="relative flex flex-row gap-1 py-2 dark:bg-black border-b last:border-none border-black/10 dark:border-white/25 leading-none px-6 overflow-hidden"
|
||||
>
|
||||
<div className="flex flex-1">
|
||||
<div className="flex flex-col">
|
||||
<span className="overflow-ellipsis overflow-hidden whitespace-nowrap">
|
||||
{file.name}
|
||||
</span>
|
||||
<span className="text-xs opacity-50 overflow-hidden text-ellipsis">
|
||||
{(file.size / 1000).toFixed(3)} kb
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
role="remove file"
|
||||
className="text-xl text-red-500 text-ellipsis absolute top-0 h-full right-0 flex items-center justify-center bg-white dark:bg-black p-3 shadow-md aspect-square"
|
||||
onClick={() =>
|
||||
setFiles((files) => files.filter((f) => f.name !== file.name))
|
||||
}
|
||||
}
|
||||
|
||||
const FileComponent = forwardRef(
|
||||
({ file, setFiles }: FileComponentProps, forwardedRef) => {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ x: "-10%", opacity: 0 }}
|
||||
animate={{ x: "0%", opacity: 1 }}
|
||||
exit={{ x: "10%", opacity: 0 }}
|
||||
layout
|
||||
ref={forwardedRef as RefObject<HTMLDivElement>}
|
||||
className="relative flex flex-row gap-1 py-2 dark:bg-black border-b last:border-none border-black/10 dark:border-white/25 leading-none px-6 overflow-hidden"
|
||||
>
|
||||
<MdClose />
|
||||
</button>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
<div className="flex flex-1">
|
||||
<div className="flex flex-col">
|
||||
<span className="overflow-ellipsis overflow-hidden whitespace-nowrap">
|
||||
{file.name}
|
||||
</span>
|
||||
<span className="text-xs opacity-50 overflow-hidden text-ellipsis">
|
||||
{(file.size / 1000).toFixed(3)} kb
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
role="remove file"
|
||||
className="text-xl text-red-500 text-ellipsis absolute top-0 h-full right-0 flex items-center justify-center bg-white dark:bg-black p-3 shadow-md aspect-square"
|
||||
onClick={() =>
|
||||
setFiles((files) => files.filter((f) => f.name !== file.name))
|
||||
}
|
||||
>
|
||||
<MdClose />
|
||||
</button>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
FileComponent.displayName = "FileComponent";
|
||||
|
||||
export default FileComponent;
|
||||
|
@ -45,7 +45,7 @@ export const useFileUploader = () => {
|
||||
});
|
||||
}
|
||||
},
|
||||
[session.access_token]
|
||||
[session.access_token, publish]
|
||||
);
|
||||
|
||||
const onDrop = (acceptedFiles: File[], fileRejections: FileRejection[]) => {
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { AnimatePresence } from "framer-motion";
|
||||
import Button from "../../../components/ui/Button";
|
||||
import Card from "../../../components/ui/Card";
|
||||
import { FileComponent } from "./components/FileComponent";
|
||||
import FileComponent from "./components/FileComponent";
|
||||
import { useFileUploader } from "./hooks/useFileUploader";
|
||||
|
||||
export const FileUploader = (): JSX.Element => {
|
||||
|
Loading…
Reference in New Issue
Block a user