diff --git a/apps/web/app/(dash)/home/page.tsx b/apps/web/app/(dash)/home/page.tsx index a78301fb..17d52529 100644 --- a/apps/web/app/(dash)/home/page.tsx +++ b/apps/web/app/(dash)/home/page.tsx @@ -57,8 +57,18 @@ function Page({
{ + if (q.length === 0) { + toast.error("Query is required"); + return; + } + const threadid = await createChatThread(q); + if (!threadid.success || !threadid.data) { + toast.error("Failed to create chat thread"); + return; + } + push( `/chat/${threadid.data}?spaces=${JSON.stringify(spaces)}&q=${q}`, ); diff --git a/apps/web/app/(dash)/home/queryinput.tsx b/apps/web/app/(dash)/home/queryinput.tsx index 99476e40..868f93f9 100644 --- a/apps/web/app/(dash)/home/queryinput.tsx +++ b/apps/web/app/(dash)/home/queryinput.tsx @@ -4,9 +4,10 @@ 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 { MultipleSelector, Option } from "@repo/ui/shadcn/combobox"; import { useRouter } from "next/navigation"; import { getSpaces } from "@/app/actions/fetchers"; +import Combobox from "@repo/ui/shadcn/combobox"; +import { MinusIcon } from "lucide-react"; function QueryInput({ initialQuery = "", @@ -53,9 +54,9 @@ function QueryInput({ ); return ( -
+
{/* input and action button */}
{ if (e.key === "Enter") { @@ -90,7 +91,7 @@ function QueryInput({ handleSubmit(q, preparedSpaces); }} disabled={disabled} - className="h-12 w-12 rounded-[14px] bg-[#21303D] all-center shrink-0 hover:brightness-125 duration-200 outline-none focus:outline focus:outline-primary active:scale-90" + 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" > Right arrow icon @@ -100,21 +101,37 @@ function QueryInput({ {!mini && ( <> -
- - setSelectedSpaces(e.map((x) => parseInt(x.value))) - } - placeholder="Focus on specific spaces..." - emptyIndicator={ -

- no results found. -

+
+ + setSelectedSpaces((prev) => { + if (v === "") { + return []; + } + return [...prev, parseInt(v)]; + }) } + onSubmit={() => {}} + placeholder="Filter spaces..." /> + +
+ {preparedSpaces.map((x, idx) => ( + + ))} +
)} diff --git a/apps/web/app/(dash)/menu.tsx b/apps/web/app/(dash)/menu.tsx index b7ea6c1c..29d25574 100644 --- a/apps/web/app/(dash)/menu.tsx +++ b/apps/web/app/(dash)/menu.tsx @@ -1,11 +1,60 @@ "use client"; -import React from "react"; +import React, { useEffect, useMemo, useState } from "react"; import Image from "next/image"; import Link from "next/link"; -import { MemoriesIcon, ExploreIcon, CanvasIcon } from "@repo/ui/icons"; +import { MemoriesIcon, ExploreIcon, CanvasIcon, AddIcon } from "@repo/ui/icons"; +import { Button } from "@repo/ui/shadcn/button"; +import { PlusCircleIcon } from "lucide-react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@repo/ui/shadcn/dialog"; +import { Label } from "@repo/ui/shadcn/label"; +import { Textarea } from "@repo/ui/shadcn/textarea"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@repo/ui/shadcn/select"; +import { toast } from "sonner"; +import { getSpaces } from "../actions/fetchers"; +import { Space } from "../actions/types"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@repo/ui/shadcn/tooltip"; +import { InformationCircleIcon } from "@heroicons/react/24/outline"; +import { createMemory } from "../actions/doers"; +import { Input } from "@repo/ui/shadcn/input"; function Menu() { + const [spaces, setSpaces] = useState([]); + + useEffect(() => { + (async () => { + let spaces = await getSpaces(); + + if (!spaces.success || !spaces.data) { + toast.warning("Unable to get spaces", { + richColors: true, + }); + setSpaces([]); + return; + } + setSpaces(spaces.data); + })(); + }, []); + const menuItems = [ { icon: MemoriesIcon, @@ -27,63 +76,231 @@ function Menu() { }, ]; + const [content, setContent] = useState(""); + const [selectedSpace, setSelectedSpace] = useState(null); + + const autoDetectedType = useMemo(() => { + if (content.length === 0) { + return "none"; + } + + if (content.match(/https?:\/\/[\w\.]+\/[\w]+\/[\w]+\/[\d]+/)) { + return "tweet"; + } else if (content.match(/https?:\/\/[\w\.]+/)) { + return "page"; + } else if (content.match(/https?:\/\/www\.[\w\.]+/)) { + return "page"; + } else { + return "note"; + } + }, [content]); + + const [dialogOpen, setDialogOpen] = useState(false); + + const handleSubmit = async (content?: string, space?: string) => { + setDialogOpen(false); + + toast.info("Creating memory..."); + + if (!content || content.length === 0) { + toast.error("Content is required"); + return; + } + + const cont = await createMemory({ + content: content, + spaces: space ? [space] : undefined, + }); + + setContent(""); + + if (cont.success) { + toast.success("Memory created"); + } else { + toast.error(`Memory creation failed: ${cont.error}`); + } + }; + return ( <> {/* Desktop Menu */} -
-
- {menuItems.map((item) => ( - - {`${item.text} -

- {item.text} -

- - ))} + +
+
+
+ + Logo +

+ Add +

+
+
+ {menuItems.map((item) => ( + + {`${item.text} +

+ {item.text} +

+ + ))} +
-
- {/* Mobile Menu */} -
-
- {menuItems.map((item) => ( - item.disabled && e.preventDefault()} + + { + const content = e.get("content")?.toString(); + const space = e.get("space")?.toString(); + + await handleSubmit(content, space); + }} + className="flex flex-col gap-4" + > + + Add memory + + A "Memory" is a bookmark, something you want to remember. + + + +
+ +