aboutsummaryrefslogtreecommitdiff
path: root/apps/web
diff options
context:
space:
mode:
authorDhravya <[email protected]>2024-06-02 13:58:40 -0500
committerDhravya <[email protected]>2024-06-02 13:58:40 -0500
commit0a282da5b12ea604d2d0480cfd19b48c6534fb4a (patch)
tree0033111273e2c6c7e99954591f1f2f625abbb336 /apps/web
parentimprovements in dash + chatUI (diff)
downloadsupermemory-0a282da5b12ea604d2d0480cfd19b48c6534fb4a.tar.xz
supermemory-0a282da5b12ea604d2d0480cfd19b48c6534fb4a.zip
added top bar nav
Diffstat (limited to 'apps/web')
-rw-r--r--apps/web/app/(dash)/actions.ts48
-rw-r--r--apps/web/app/(dash)/chat/chatWindow.tsx8
-rw-r--r--apps/web/app/(dash)/chat/page.tsx6
-rw-r--r--apps/web/app/(dash)/header.tsx32
-rw-r--r--apps/web/app/(dash)/home/page.tsx7
-rw-r--r--apps/web/app/(dash)/home/queryinput.tsx36
-rw-r--r--apps/web/app/(dash)/layout.tsx11
-rw-r--r--apps/web/app/helpers/lib/searchParams.ts11
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([]),
});