Refactor to use hooks (#436)
This commit is contained in:
parent
124efca9a1
commit
cb60f8b143
139 changed files with 8871 additions and 8726 deletions
241
components/custom/history.tsx
Normal file
241
components/custom/history.tsx
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
"use client";
|
||||
|
||||
import * as VisuallyHidden from "@radix-ui/react-visually-hidden";
|
||||
import cx from "classnames";
|
||||
import Link from "next/link";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
import { User } from "next-auth";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import useSWR from "swr";
|
||||
|
||||
import { Chat } from "@/db/schema";
|
||||
import { fetcher } from "@/lib/utils";
|
||||
|
||||
import {
|
||||
InfoIcon,
|
||||
MenuIcon,
|
||||
MoreHorizontalIcon,
|
||||
PencilEditIcon,
|
||||
TrashIcon,
|
||||
} from "./icons";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "../ui/alert-dialog";
|
||||
import { Button } from "../ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "../ui/dropdown-menu";
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
} from "../ui/sheet";
|
||||
|
||||
export const History = ({ user }: { user: User | undefined }) => {
|
||||
const { id } = useParams();
|
||||
const pathname = usePathname();
|
||||
|
||||
const [isHistoryVisible, setIsHistoryVisible] = useState(false);
|
||||
const {
|
||||
data: history,
|
||||
isLoading,
|
||||
mutate,
|
||||
} = useSWR<Array<Chat>>(user ? "/api/history" : null, fetcher, {
|
||||
fallbackData: [],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
mutate();
|
||||
}, [pathname, mutate]);
|
||||
|
||||
const [deleteId, setDeleteId] = useState<string | null>(null);
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
|
||||
const handleDelete = async () => {
|
||||
const deletePromise = fetch(`/api/chat?id=${deleteId}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
toast.promise(deletePromise, {
|
||||
loading: "Deleting chat...",
|
||||
success: () => {
|
||||
mutate((history) => {
|
||||
if (history) {
|
||||
return history.filter((h) => h.id !== id);
|
||||
}
|
||||
});
|
||||
return "Chat deleted successfully";
|
||||
},
|
||||
error: "Failed to delete chat",
|
||||
});
|
||||
|
||||
setShowDeleteDialog(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="p-1.5 h-fit"
|
||||
onClick={() => {
|
||||
setIsHistoryVisible(true);
|
||||
}}
|
||||
>
|
||||
<MenuIcon />
|
||||
</Button>
|
||||
|
||||
<Sheet
|
||||
open={isHistoryVisible}
|
||||
onOpenChange={(state) => {
|
||||
setIsHistoryVisible(state);
|
||||
}}
|
||||
>
|
||||
<SheetContent side="left" className="p-3 w-80 bg-muted">
|
||||
<SheetHeader>
|
||||
<VisuallyHidden.Root>
|
||||
<SheetTitle className="text-left">History</SheetTitle>
|
||||
<SheetDescription className="text-left">
|
||||
{history === undefined ? "loading" : history.length} chats
|
||||
</SheetDescription>
|
||||
</VisuallyHidden.Root>
|
||||
</SheetHeader>
|
||||
|
||||
<div className="text-sm flex flex-row items-center justify-between">
|
||||
<div className="flex flex-row gap-2">
|
||||
<div className="dark:text-zinc-300">History</div>
|
||||
|
||||
<div className="dark:text-zinc-400 text-zinc-500">
|
||||
{history === undefined ? "loading" : history.length} chats
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 flex flex-col">
|
||||
{user && (
|
||||
<Button
|
||||
className="font-normal text-sm flex flex-row justify-between"
|
||||
asChild
|
||||
>
|
||||
<Link href="/">
|
||||
<div>Start a new chat</div>
|
||||
<PencilEditIcon size={14} />
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col overflow-y-scroll p-1 h-[calc(100dvh-124px)]">
|
||||
{!user ? (
|
||||
<div className="text-zinc-500 h-dvh w-full flex flex-row justify-center items-center text-sm gap-2">
|
||||
<InfoIcon />
|
||||
<div>Login to save and revisit previous chats!</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!isLoading && history?.length === 0 && user ? (
|
||||
<div className="text-zinc-500 h-dvh w-full flex flex-row justify-center items-center text-sm gap-2">
|
||||
<InfoIcon />
|
||||
<div>No chats found</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{isLoading && user ? (
|
||||
<div className="flex flex-col">
|
||||
{[44, 32, 28, 52].map((item) => (
|
||||
<div key={item} className="p-2 my-[2px]">
|
||||
<div
|
||||
className={`w-${item} h-[20px] rounded-md bg-zinc-200 dark:bg-zinc-600 animate-pulse`}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{history &&
|
||||
history.map((chat) => (
|
||||
<div
|
||||
key={chat.id}
|
||||
className={cx(
|
||||
"flex flex-row items-center gap-6 hover:bg-zinc-200 dark:hover:bg-zinc-700 rounded-md pr-2",
|
||||
{ "bg-zinc-200 dark:bg-zinc-700": chat.id === id },
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={cx(
|
||||
"hover:bg-zinc-200 dark:hover:bg-zinc-700 justify-between p-0 text-sm font-normal flex flex-row items-center gap-2 pr-2 w-full transition-none",
|
||||
)}
|
||||
asChild
|
||||
>
|
||||
<Link
|
||||
href={`/chat/${chat.id}`}
|
||||
className="text-ellipsis overflow-hidden text-left py-2 pl-2 rounded-lg outline-zinc-900"
|
||||
>
|
||||
{chat.messages[0].content as string}
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
<DropdownMenu modal={true}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
className="p-0 h-fit font-normal text-zinc-500 transition-none hover:bg-zinc-200 dark:hover:bg-zinc-700"
|
||||
variant="ghost"
|
||||
>
|
||||
<MoreHorizontalIcon />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent side="left" className="z-[60]">
|
||||
<DropdownMenuItem asChild>
|
||||
<Button
|
||||
className="flex flex-row gap-2 items-center justify-start w-full h-fit font-normal p-1.5 rounded-sm"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
setDeleteId(chat.id);
|
||||
setShowDeleteDialog(true);
|
||||
}}
|
||||
>
|
||||
<TrashIcon />
|
||||
<div>Delete</div>
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
|
||||
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone. This will permanently delete your
|
||||
chat and remove it from our servers.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleDelete}>
|
||||
Continue
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue