diff options
| author | Dhravya <[email protected]> | 2024-06-02 13:58:40 -0500 |
|---|---|---|
| committer | Dhravya <[email protected]> | 2024-06-02 13:58:40 -0500 |
| commit | 0a282da5b12ea604d2d0480cfd19b48c6534fb4a (patch) | |
| tree | 0033111273e2c6c7e99954591f1f2f625abbb336 /apps/web | |
| parent | improvements in dash + chatUI (diff) | |
| download | supermemory-0a282da5b12ea604d2d0480cfd19b48c6534fb4a.tar.xz supermemory-0a282da5b12ea604d2d0480cfd19b48c6534fb4a.zip | |
added top bar nav
Diffstat (limited to 'apps/web')
| -rw-r--r-- | apps/web/app/(dash)/actions.ts | 48 | ||||
| -rw-r--r-- | apps/web/app/(dash)/chat/chatWindow.tsx | 8 | ||||
| -rw-r--r-- | apps/web/app/(dash)/chat/page.tsx | 6 | ||||
| -rw-r--r-- | apps/web/app/(dash)/header.tsx | 32 | ||||
| -rw-r--r-- | apps/web/app/(dash)/home/page.tsx | 7 | ||||
| -rw-r--r-- | apps/web/app/(dash)/home/queryinput.tsx | 36 | ||||
| -rw-r--r-- | apps/web/app/(dash)/layout.tsx | 11 | ||||
| -rw-r--r-- | apps/web/app/helpers/lib/searchParams.ts | 11 |
8 files changed, 124 insertions, 35 deletions
diff --git a/apps/web/app/(dash)/actions.ts b/apps/web/app/(dash)/actions.ts new file mode 100644 index 00000000..70c2a567 --- /dev/null +++ b/apps/web/app/(dash)/actions.ts @@ -0,0 +1,48 @@ +"use server"; + +import { cookies, headers } from "next/headers"; +import { db } from "../helpers/server/db"; +import { sessions, users, space } from "../helpers/server/db/schema"; +import { eq } from "drizzle-orm"; +import { redirect } from "next/navigation"; + +export async function ensureAuth() { + const token = + cookies().get("next-auth.session-token")?.value ?? + cookies().get("__Secure-authjs.session-token")?.value ?? + cookies().get("authjs.session-token")?.value ?? + headers().get("Authorization")?.replace("Bearer ", ""); + + if (!token) { + return undefined; + } + + const sessionData = await db + .select() + .from(sessions) + .innerJoin(users, eq(users.id, sessions.userId)) + .where(eq(sessions.sessionToken, token)); + + if (!sessionData || sessionData.length < 0) { + return undefined; + } + + return { + user: sessionData[0]!.user, + session: sessionData[0]!, + }; +} + +export async function getSpaces() { + const data = await ensureAuth(); + if (!data) { + redirect("/signin"); + } + + const sp = await db + .select() + .from(space) + .where(eq(space.user, data.user.email)); + + return sp; +} diff --git a/apps/web/app/(dash)/chat/chatWindow.tsx b/apps/web/app/(dash)/chat/chatWindow.tsx index 8361bbf8..43c337ee 100644 --- a/apps/web/app/(dash)/chat/chatWindow.tsx +++ b/apps/web/app/(dash)/chat/chatWindow.tsx @@ -7,7 +7,7 @@ import { cn } from "@repo/ui/lib/utils"; import { motion } from "framer-motion"; import { useRouter } from "next/navigation"; -function ChatWindow({ q, spaces }: { q: string; spaces: number[] }) { +function ChatWindow({ q }: { q: string }) { const [layout, setLayout] = useState<"chat" | "initial">("initial"); const router = useRouter(); @@ -31,11 +31,7 @@ function ChatWindow({ q, spaces }: { q: string; spaces: number[] }) { className="max-w-3xl flex mx-auto w-full flex-col" > <div className="w-full h-96"> - <QueryInput - initialQuery={q} - initialSpaces={spaces ?? []} - disabled - /> + <QueryInput initialQuery={q} initialSpaces={[]} disabled /> </div> </motion.div> ) : ( diff --git a/apps/web/app/(dash)/chat/page.tsx b/apps/web/app/(dash)/chat/page.tsx index 4ad4d468..9e28fda7 100644 --- a/apps/web/app/(dash)/chat/page.tsx +++ b/apps/web/app/(dash)/chat/page.tsx @@ -1,5 +1,5 @@ -import { chatSearchParamsCache } from "../../helpers/lib/searchParams"; import ChatWindow from "./chatWindow"; +import { chatSearchParamsCache } from "../../helpers/lib/searchParams"; function Page({ searchParams, @@ -8,7 +8,9 @@ function Page({ }) { const { firstTime, q, spaces } = chatSearchParamsCache.parse(searchParams); - return <ChatWindow q={q} spaces={spaces ?? []} />; + console.log(spaces); + + return <ChatWindow q={q} />; } export default Page; diff --git a/apps/web/app/(dash)/header.tsx b/apps/web/app/(dash)/header.tsx index bcbe9691..104c63bc 100644 --- a/apps/web/app/(dash)/header.tsx +++ b/apps/web/app/(dash)/header.tsx @@ -3,6 +3,7 @@ import Image from "next/image"; import Link from "next/link"; import Logo from "../../public/logo.svg"; import { AddIcon, ChatIcon } from "@repo/ui/icons"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@repo/ui/shadcn/tabs"; function Header() { return ( @@ -16,11 +17,32 @@ function Header() { /> </Link> - <div className="absolute flex justify-center w-full -z-10"> - <button className="bg-secondary all-center h-11 rounded-full p-2 min-w-14"> - <Image src={AddIcon} alt="Add icon" /> - </button> - </div> + <Tabs + className="absolute flex flex-col justify-center items-center w-full -z-10 group top-0 transition-transform duration-1000 ease-out" + defaultValue="account" + > + <div className="bg-secondary all-center h-11 rounded-full p-2 min-w-14"> + <button className="p-2 group-hover:hidden transition duration-500 ease-in-out"> + <Image src={AddIcon} alt="Add icon" /> + </button> + + <div className="hidden group-hover:flex inset-0 transition-opacity duration-500 ease-in-out"> + <TabsList className="p-2"> + <TabsTrigger value="account">Account</TabsTrigger> + <TabsTrigger value="password">Password</TabsTrigger> + </TabsList> + </div> + </div> + + <div className="bg-secondary all-center rounded-full p-2 mt-4 min-w-14 hidden group-hover:block"> + <TabsContent value="account"> + Make changes to your account here. + </TabsContent> + <TabsContent value="password"> + Change your password here. + </TabsContent> + </div> + </Tabs> <button className="flex shrink-0 duration-200 items-center gap-2 px-2 py-1.5 rounded-xl hover:bg-secondary"> <Image src={ChatIcon} alt="Chat icon" /> diff --git a/apps/web/app/(dash)/home/page.tsx b/apps/web/app/(dash)/home/page.tsx index a6624c1b..7a6bb94f 100644 --- a/apps/web/app/(dash)/home/page.tsx +++ b/apps/web/app/(dash)/home/page.tsx @@ -3,8 +3,9 @@ import Menu from "../menu"; import Header from "../header"; import QueryInput from "./queryinput"; import { homeSearchParamsCache } from "@/app/helpers/lib/searchParams"; +import { getSpaces } from "../actions"; -function Page({ +async function Page({ searchParams, }: { searchParams: Record<string, string | string[] | undefined>; @@ -12,13 +13,15 @@ function Page({ // TODO: use this to show a welcome page/modal const { firstTime } = homeSearchParamsCache.parse(searchParams); + const spaces = await getSpaces(); + return ( <div className="max-w-3xl flex mx-auto w-full flex-col"> {/* all content goes here */} {/* <div className="">hi {firstTime ? 'first time' : ''}</div> */} <div className="w-full h-96"> - <QueryInput /> + <QueryInput initialSpaces={spaces} /> </div> </div> ); diff --git a/apps/web/app/(dash)/home/queryinput.tsx b/apps/web/app/(dash)/home/queryinput.tsx index 904fcca6..d098fda8 100644 --- a/apps/web/app/(dash)/home/queryinput.tsx +++ b/apps/web/app/(dash)/home/queryinput.tsx @@ -7,43 +7,45 @@ import Divider from "@repo/ui/shadcn/divider"; import { MultipleSelector, Option } from "@repo/ui/shadcn/combobox"; import { useRouter } from "next/navigation"; -const OPTIONS: Option[] = [ - { label: "nextjs", value: "0" }, - { label: "React", value: "1" }, - { label: "Remix", value: "2" }, - { label: "Vite", value: "3" }, - { label: "Nuxt", value: "4" }, - { label: "Vue", value: "5" }, - { label: "Svelte", value: "6" }, - { label: "Angular", value: "7" }, - { label: "Ember", value: "8" }, - { label: "Gatsby", value: "9" }, -]; - function QueryInput({ initialQuery = "", initialSpaces = [], disabled = false, }: { initialQuery?: string; - initialSpaces?: number[]; + initialSpaces?: { user: string | null; id: number; name: string }[]; disabled?: boolean; }) { const [q, setQ] = useState(initialQuery); - const [selectedSpaces, setSelectedSpaces] = useState<number[]>(initialSpaces); + const [selectedSpaces, setSelectedSpaces] = useState<number[]>([]); const { push } = useRouter(); const parseQ = () => { + // preparedSpaces is list of spaces selected by user, with id and name + const preparedSpaces = initialSpaces + .filter((x) => selectedSpaces.includes(x.id)) + .map((x) => { + return { + id: x.id, + name: x.name, + }; + }); + const newQ = "/chat?q=" + encodeURI(q) + - (selectedSpaces ? "&spaces=" + selectedSpaces.join(",") : ""); + (selectedSpaces ? "&spaces=" + JSON.stringify(preparedSpaces) : ""); return newQ; }; + const options = initialSpaces.map((x) => ({ + label: x.name, + value: x.id.toString(), + })); + return ( <div> <div className="bg-secondary rounded-t-[24px] w-full mt-40"> @@ -81,7 +83,7 @@ function QueryInput({ <div className="flex items-center gap-6 p-2 h-auto bg-secondary rounded-b-[24px]"> <MultipleSelector disabled={disabled} - defaultOptions={OPTIONS} + defaultOptions={options} onChange={(e) => setSelectedSpaces(e.map((x) => parseInt(x.value)))} placeholder="Focus on specific spaces..." emptyIndicator={ diff --git a/apps/web/app/(dash)/layout.tsx b/apps/web/app/(dash)/layout.tsx index 07c16485..dffa27fa 100644 --- a/apps/web/app/(dash)/layout.tsx +++ b/apps/web/app/(dash)/layout.tsx @@ -1,8 +1,15 @@ -import React from "react"; import Header from "./header"; import Menu from "./menu"; +import { ensureAuth } from "./actions"; +import { redirect } from "next/navigation"; + +async function Layout({ children }: { children: React.ReactNode }) { + const info = await ensureAuth(); + + if (!info) { + return redirect("/signin"); + } -function Layout({ children }: { children: React.ReactNode }) { return ( <main className="h-screen flex flex-col p-4 relative"> <Header /> diff --git a/apps/web/app/helpers/lib/searchParams.ts b/apps/web/app/helpers/lib/searchParams.ts index 2e02aa3e..9899eaf7 100644 --- a/apps/web/app/helpers/lib/searchParams.ts +++ b/apps/web/app/helpers/lib/searchParams.ts @@ -4,7 +4,9 @@ import { parseAsString, parseAsBoolean, parseAsArrayOf, + parseAsJson, } from "nuqs/server"; +import { z } from "zod"; export const homeSearchParamsCache = createSearchParamsCache({ firstTime: parseAsBoolean.withDefault(false), @@ -13,5 +15,12 @@ export const homeSearchParamsCache = createSearchParamsCache({ export const chatSearchParamsCache = createSearchParamsCache({ firstTime: parseAsBoolean.withDefault(false), q: parseAsString.withDefault(""), - spaces: parseAsArrayOf(parseAsInteger, ","), + spaces: parseAsArrayOf( + parseAsJson(() => + z.object({ + id: z.string(), + name: z.string(), + }), + ), + ).withDefault([]), }); |