diff options
| author | Dhravya Shah <[email protected]> | 2024-07-20 20:49:33 -0500 |
|---|---|---|
| committer | Dhravya Shah <[email protected]> | 2024-07-20 20:49:33 -0500 |
| commit | 4c96fa349c6fe2409a54ee7bb6c8a43b85a4e109 (patch) | |
| tree | bf56c8602b599f46b3667fd5ff8d49567b365a9b /apps/web/app/(dash)/chat | |
| parent | remove everything inside raw tags (diff) | |
| download | supermemory-4c96fa349c6fe2409a54ee7bb6c8a43b85a4e109.tar.xz supermemory-4c96fa349c6fe2409a54ee7bb6c8a43b85a4e109.zip | |
changes in queryinput home layout
Diffstat (limited to 'apps/web/app/(dash)/chat')
| -rw-r--r-- | apps/web/app/(dash)/chat/chatQueryInput.tsx | 181 | ||||
| -rw-r--r-- | apps/web/app/(dash)/chat/chatWindow.tsx | 6 |
2 files changed, 183 insertions, 4 deletions
diff --git a/apps/web/app/(dash)/chat/chatQueryInput.tsx b/apps/web/app/(dash)/chat/chatQueryInput.tsx new file mode 100644 index 00000000..c7267298 --- /dev/null +++ b/apps/web/app/(dash)/chat/chatQueryInput.tsx @@ -0,0 +1,181 @@ +"use client"; + +import { ArrowRightIcon } from "@repo/ui/icons"; +import Image from "next/image"; +import React, { useEffect, useMemo, useState } from "react"; +import Divider from "@repo/ui/shadcn/divider"; +import { useRouter } from "next/navigation"; +import { getSpaces } from "@/app/actions/fetchers"; +import Combobox from "@repo/ui/shadcn/combobox"; +import { MinusIcon } from "lucide-react"; +import { toast } from "sonner"; +import { createSpace } from "@/app/actions/doers"; + +function QueryInput({ + initialQuery = "", + initialSpaces = [], + disabled = false, + className, + mini = false, + handleSubmit, + setInitialSpaces, +}: { + initialQuery?: string; + initialSpaces?: { + id: number; + name: string; + }[]; + disabled?: boolean; + className?: string; + mini?: boolean; + handleSubmit: (q: string, spaces: { id: number; name: string }[]) => void; + setInitialSpaces?: React.Dispatch< + React.SetStateAction<{ id: number; name: string }[]> + >; +}) { + const [q, setQ] = useState(initialQuery); + + const [selectedSpaces, setSelectedSpaces] = useState<number[]>([]); + + const options = useMemo( + () => + initialSpaces.map((x) => ({ + label: x.name, + value: x.id.toString(), + })), + [initialSpaces], + ); + + const preparedSpaces = useMemo( + () => + initialSpaces + .filter((x) => selectedSpaces.includes(x.id)) + .map((x) => { + return { + id: x.id, + name: x.name, + }; + }), + [selectedSpaces, initialSpaces], + ); + + return ( + <div className={`${className}`}> + <div + className={`bg-secondary border-2 border-b-0 border-border ${!mini ? "rounded-t-3xl" : "rounded-3xl"}`} + > + {/* input and action button */} + <form + action={async () => { + handleSubmit(q, preparedSpaces); + setQ(""); + }} + className="flex gap-4 p-3" + > + <textarea + autoFocus + name="q" + cols={30} + rows={mini ? 2 : 4} + className="bg-transparent pt-2.5 text-base placeholder:text-[#9B9B9B] focus:text-gray-200 duration-200 tracking-[3%] outline-none resize-none w-full p-4" + placeholder="Ask your second brain..." + onKeyDown={(e) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + if (q.trim().length === 0) { + return; + } + handleSubmit(q, preparedSpaces); + setQ(""); + } + }} + onChange={(e) => setQ(e.target.value)} + value={q} + disabled={disabled} + /> + + <button + type="submit" + onClick={(e) => { + e.preventDefault(); + if (q.trim().length === 0) { + return; + } + handleSubmit(q, preparedSpaces); + }} + disabled={disabled} + className="h-12 w-12 rounded-[14px] bg-border all-center shrink-0 hover:brightness-125 duration-200 outline-none focus:outline focus:outline-primary active:scale-90" + > + <Image src={ArrowRightIcon} alt="Right arrow icon" /> + </button> + </form> + </div> + {/* selected sources */} + {!mini && ( + <> + <Divider /> + <div className="flex justify-between items-center gap-6 h-auto bg-secondary rounded-b-3xl border-2 border-border"> + <Combobox + options={options} + className="rounded-bl-3xl bg-[#3C464D] w-44" + onSelect={(v) => + setSelectedSpaces((prev) => { + if (v === "") { + return []; + } + return [...prev, parseInt(v)]; + }) + } + onSubmit={async (spaceName) => { + const space = options.find((x) => x.label === spaceName); + toast.info("Creating space..."); + + if (space) { + toast.error("A space with that name already exists."); + } + + const creationTask = await createSpace(spaceName); + if (creationTask.success && creationTask.data) { + toast.success("Space created " + creationTask.data); + setInitialSpaces?.((prev) => [ + ...prev, + { + name: spaceName, + id: creationTask.data!, + }, + ]); + setSelectedSpaces((prev) => [...prev, creationTask.data!]); + } else { + toast.error( + "Space creation failed: " + creationTask.error ?? + "Unknown error", + ); + } + }} + placeholder="Chat with a space..." + /> + + <div className="flex flex-row gap-0.5 h-full"> + {preparedSpaces.map((x, idx) => ( + <button + key={x.id} + onClick={() => + setSelectedSpaces((prev) => prev.filter((y) => y !== x.id)) + } + className={`relative group p-2 py-3 bg-[#3C464D] max-w-32 ${idx === preparedSpaces.length - 1 ? "rounded-br-xl" : ""}`} + > + <p className="line-clamp-1">{x.name}</p> + <div className="absolute h-full right-0 top-0 p-1 opacity-0 group-hover:opacity-100 items-center"> + <MinusIcon className="w-6 h-6 rounded-full bg-secondary" /> + </div> + </button> + ))} + </div> + </div> + </> + )} + </div> + ); +} + +export default QueryInput; diff --git a/apps/web/app/(dash)/chat/chatWindow.tsx b/apps/web/app/(dash)/chat/chatWindow.tsx index f0827a3d..0c1bace0 100644 --- a/apps/web/app/(dash)/chat/chatWindow.tsx +++ b/apps/web/app/(dash)/chat/chatWindow.tsx @@ -2,7 +2,7 @@ import { AnimatePresence } from "framer-motion"; import React, { useEffect, useRef, useState } from "react"; -import QueryInput from "../home/queryinput"; +import QueryInput from "./chatQueryInput"; import { cn } from "@repo/ui/lib/utils"; import { motion } from "framer-motion"; import { useRouter } from "next/navigation"; @@ -24,7 +24,6 @@ import { toast } from "sonner"; import Link from "next/link"; import { createChatObject } from "@/app/actions/doers"; import { ClipboardIcon } from "@heroicons/react/24/outline"; -import { SendIcon } from "lucide-react"; function ChatWindow({ q, @@ -199,9 +198,8 @@ function ChatWindow({ <div className="w-full h-96"> <QueryInput handleSubmit={() => {}} - initialQuery={q} initialSpaces={[]} - disabled + initialQuery={q} /> </div> </motion.div> |